Browse Source

冲突解决

master
wenlele 1 year ago
parent
commit
83ed9475d0
  1. 164
      api/app/lib/controllers/member/index.js
  2. 10
      api/app/lib/models/user.js
  3. 23
      api/app/lib/routes/member/index.js
  4. 4
      scripts/0.0.6/01_alter_user.sql
  5. 5
      web/client/src/app.js
  6. 36
      web/client/src/layout/components/header/index.js
  7. 6
      web/client/src/sections/memberManagement/actions/index.js
  8. 56
      web/client/src/sections/memberManagement/actions/member.js
  9. 69
      web/client/src/sections/memberManagement/actions/task.js
  10. 78
      web/client/src/sections/memberManagement/components/memberModal.js
  11. 61
      web/client/src/sections/memberManagement/components/resetPassword.js
  12. 6
      web/client/src/sections/memberManagement/components/style.less
  13. 26
      web/client/src/sections/memberManagement/components/taskModal.js
  14. 5
      web/client/src/sections/memberManagement/containers/index.js
  15. 230
      web/client/src/sections/memberManagement/containers/member.js
  16. 5
      web/client/src/sections/memberManagement/containers/style.less
  17. 15
      web/client/src/sections/memberManagement/index.js
  18. 17
      web/client/src/sections/memberManagement/nav-item.js
  19. 5
      web/client/src/sections/memberManagement/reducers/index.js
  20. 17
      web/client/src/sections/memberManagement/routes.js
  21. 4
      web/client/src/sections/metadataAcquisition/nav-item.js
  22. 67
      web/client/src/sections/metadataManagement/containers/databasesTable.js
  23. 70
      web/client/src/sections/metadataManagement/containers/filesTable.js
  24. 4
      web/client/src/sections/metadataManagement/containers/latestMetadata.js
  25. 52
      web/client/src/sections/metadataManagement/containers/restapisTable.js
  26. 6
      web/client/src/utils/webapi.js

164
api/app/lib/controllers/member/index.js

@ -0,0 +1,164 @@
'use strict';
const Hex = require('crypto-js/enc-hex');
const MD5 = require('crypto-js/md5');
function getUserList(opts) {
return async function (ctx, next) {
const models = ctx.fs.dc.models;
const { page, limit, name, role } = ctx.query;
const Op = ctx.fs.dc.ORM.Op;
let errMsg = { message: '获取用户失败' }
try {
let searchWhere = {
username: { $not: 'SuperAdmin' }
}
let option = {
where: searchWhere,
order: [["id", "desc"]],
attributes: { exclude: ['password'] },
}
if (name) {
searchWhere.name = { $like: '%' + name + '%' };
}
if (role) {
searchWhere.role = role;
}
option.where = searchWhere
let limit_ = limit || 10;
let page_ = page || 1;
let offset = (page_ - 1) * limit_;
if (limit && page) {
option.limit = limit_
option.offset = offset
}
const res = await models.User.findAndCount(option);
ctx.status = 200;
ctx.body = res;
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = errMsg
}
}
}
// 新增用户
function addUser(opts) {
return async function (ctx, next) {
const models = ctx.fs.dc.models;
try {
const { username } = ctx.request.body
const checkName = await models.User.findOne({ where: { username } });
if (checkName) {
ctx.status = 400;
ctx.body = { message: "该用户名已存在" }
} else {
let rslt = ctx.request.body;
rslt.password = 'e10adc3949ba59abbe56e057f20f883e';
await models.User.create(Object.assign({}, rslt))
ctx.status = 204;
ctx.body = { message: '新建用户成功' }
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '新建用户失败' }
}
}
}
// 修改用户
function editUser(opts) {
return async function (ctx, next) {
try {
const models = ctx.fs.dc.models;
const { id } = ctx.params;
const body = ctx.request.body;
if (body.oldpassword) {
const password = Hex.stringify(MD5(body.oldpassword));
const checkPwd = await models.User.findOne({ where: { id: id, password } });
if (!checkPwd) {
ctx.status = 400;
ctx.body = { message: '旧密码错误' }
} else {
await models.User.update(
{ password: Hex.stringify(MD5(body.password)) },
{ where: { id: id, } }
)
ctx.status = 204;
ctx.body = { message: '修改用户成功' }
}
} else {
const checkName = await models.User.findOne({ where: { id: { $not: id }, username: body.username } });
if (checkName) {
ctx.status = 400;
ctx.body = { message: '该用户名已存在' }
} else {
await models.User.update(
body,
{ where: { id: id, } }
)
ctx.status = 204;
ctx.body = { message: '修改用户成功' }
}
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '修改用户失败' }
}
}
}
// 删除用户
function deleteUser(opts) {
return async function (ctx, next) {
try {
const models = ctx.fs.dc.models;
const { id } = ctx.params;
const checkName1 = await models.MetadataDatabase.findOne({ where: { createBy: id } });
const checkName2 = await models.MetadataFile.findOne({ where: { createBy: id } });
const checkName3 = await models.MetadataRestapi.findOne({ where: { createBy: id } });
if (checkName1 || checkName2 || checkName3) {
ctx.status = 400;
ctx.body = { message: '该用户下存在依赖资源无法删除!' }
} else {
await models.User.destroy({
where: {
id: id
}
})
ctx.status = 204;
ctx.body = { message: '删除用户成功' }
}
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = { message: '删除用户失败' }
}
}
}
module.exports = {
getUserList,
addUser,
editUser,
deleteUser,
}

10
api/app/lib/models/user.js

@ -51,7 +51,17 @@ module.exports = dc => {
primaryKey: false,
field: "role",
autoIncrement: false
},
enabled: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: true,
comment: "是否启用",
primaryKey: false,
field: "enabled",
autoIncrement: false
}
}, {
tableName: "t_user",
comment: "",

23
api/app/lib/routes/member/index.js

@ -0,0 +1,23 @@
'use strict';
const member = require('../../controllers/member/index');
module.exports = function (app, router, opts, AuthCode) {
app.fs.api.logAttr['POST/meta/member'] = { content: '增加用户', visible: true };
router.post('/meta/member', member.addUser(opts))
// 修改用户信息
app.fs.api.logAttr['PUT/meta/member/:id'] = { content: '修改用户信息', visible: true };
router.put('/meta/member/:id', member.editUser(opts))
// 删除用户信息
app.fs.api.logAttr['DEL/meta/member/:id'] = { content: '删除用户信息', visible: true };
router.del('/meta/member/:id', member.deleteUser(opts))
//获取用户信息列表
app.fs.api.logAttr['GET/meta/members'] = { content: '获取用户信息列表', visible: true };
router.get('/meta/members', member.getUserList(opts));
};

4
scripts/0.0.6/01_alter_user.sql

@ -0,0 +1,4 @@
alter table t_user
add enabled boolean default true;
comment on column t_user.enabled is '是否启用';

5
web/client/src/app.js

@ -8,6 +8,7 @@ import MetadataManagement from './sections/metadataManagement';
import MetadataAcquisition from './sections/metadataAcquisition';
import resourceConsumption from './sections/resourceConsumption';
import resourceRetrieval from './sections/resourceRetrieval';
import memberManagement from './sections/memberManagement';
const App = props => {
const { projectName } = props
@ -24,7 +25,9 @@ const App = props => {
MetadataManagement,
MetadataAcquisition,
resourceConsumption,
resourceRetrieval]}
resourceRetrieval,
memberManagement
]}
/>
)

36
web/client/src/layout/components/header/index.js

@ -7,10 +7,23 @@ import styles from './style.css';
import {
MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LogoutOutlined
} from '@ant-design/icons';
import ResetPasswordModal from '../../../sections/memberManagement/components/resetPassword';
const Header = props => {
const { dispatch, history, user, pathname, toggleCollapsed, collapsed, actions } = props
const onFinish = async (values) => {
const dataToSave = { ...values }
return dispatch(
actions.memberManagement.modifyUser(user.id, dataToSave, values?.msg || ''),
).then((res) => {
if (res.success) {
return true;
} else {
return false;
}
});
};
const handelClick = item => {
if (item.key == 'logout') {
dispatch(actions.auth.logout(user));
@ -53,9 +66,22 @@ const Header = props => {
label: <span style={{ color: 'aliceblue' }}>{user.displayName}</span>,
key: "user",
icon: <img className={styles['header-nav-user-img']} src={`/assets/images/avatar/5.png`} />,
children: [{
label: '退出', key: 'logout'
}],
children: [
{
icon: <UserOutlined />,
label: <ResetPasswordModal
editData={user}
triggerRender={<a>修改密码</a>}
title="修改密码"
onFinish={onFinish}
key="resetPassword"
/>,
key: 'resetPassword'
},
{
label: '退出', key: 'logout', icon: <LogoutOutlined />
},
],
}]}
/>
</div>
@ -63,7 +89,7 @@ const Header = props => {
);
};
function mapStateToProps (state) {
function mapStateToProps(state) {
const { global, auth } = state;
return {
actions: global.actions,

6
web/client/src/sections/memberManagement/actions/index.js

@ -0,0 +1,6 @@
'use strict';
import * as member from './member';
export default {
...member
}

56
web/client/src/sections/memberManagement/actions/member.js

@ -0,0 +1,56 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function getUserList(query) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query || {},
actionType: 'GET_MEMBER_REPORT',
url: `${ApiTable.getUserList}`,
msg: { error: '获取用户列表失败' },
reducer: { name: 'member' }
});
}
export function addUser(params) {
return (dispatch) => basicAction({
type: 'post',
data: params,
dispatch,
actionType: 'ADD_MEMBER_REPORT',
url: ApiTable.addUser,
msg: {
option: '用户新增',
},
});
}
export function deleteUser(id) {
return (dispatch) => basicAction({
type: 'del',
dispatch,
actionType: 'DELETE_MEMBER_REPORT',
url: ApiTable.modifyUser.replace('{id}', id),
msg: {
option: '用户删除',
},
});
}
export function modifyUser(id, params, msg) {
return (dispatch) => basicAction({
type: 'put',
data: params,
dispatch,
actionType: 'MODIFY_MEMBER_REPORT',
url: ApiTable.modifyUser.replace('{id}', id),
msg: {
option: msg || '用户编辑',
},
});
}

69
web/client/src/sections/memberManagement/actions/task.js

@ -0,0 +1,69 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function addTask(params, msg) {
return (dispatch) => basicAction({
type: 'post',
data: params,
dispatch,
actionType: 'ADD_ACQ_TASK',
url: ApiTable.addTask,
msg: {
option: msg || '新增采集任务',
},
});
}
export function getTasks(query) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query || {},
actionType: 'GET_ACQ_TASKS',
url: `${ApiTable.getTasks}`,
msg: { error: '获取采集任务列表失败' },
reducer: { name: 'tasks' }
});
}
export function deleteTask(id) {
return (dispatch) => basicAction({
type: 'del',
dispatch,
actionType: 'DELETE_ACQ_TASK',
url: ApiTable.modifyTask.replace('{id}', id),
msg: {
option: '采集任务删除',
},
});
}
export function modifyTask(id, params, msg) {
return (dispatch) => basicAction({
type: 'put',
data: params,
dispatch,
actionType: 'MODIFY_ACQ_TASK',
url: ApiTable.modifyTask.replace('{id}', id),
msg: {
option: msg || '采集任务编辑',
},
});
}
export function runTask(params, msg) {
return (dispatch) => basicAction({
type: 'post',
data: params,
dispatch,
actionType: 'RUN_ACQ_TASK',
url: ApiTable.runTask,
msg: {
option: msg || '任务执行',
},
});
}

78
web/client/src/sections/memberManagement/components/memberModal.js

@ -0,0 +1,78 @@
import React, { useRef } from 'react';
import { Button, Form } from 'antd';
import { InfoCircleOutlined } from '@ant-design/icons';
import {
ModalForm,
ProFormSelect,
ProFormTextArea,
ProFormDigit,
ProFormText,
ProFormSwitch
} from '@ant-design/pro-form';
export default (props) => {
const { title, triggerRender, editData = null, onFinish, paramsName } = props;
const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
const initialValues = editData ? {
...editData,
} : {};
const [form] = Form.useForm();
const formRef = useRef();
return (
<ModalForm
formRef={formRef}
title={title || ''}
initialValues={initialValues}
trigger={
triggerRender ? triggerRender : <Button type="primary" >
{title || ''}
</Button>
}
layout="horizontal"
grid={true}
{...formItemLayout}
modalProps={{
destroyOnClose: true,
onCancel: () => { },
}}
onFinish={async (values) => {
return onFinish && await onFinish(values, editData, form)
// return true;
}}
width={500}
>
<ProFormText
rules={[{ required: true, message: '请输入姓名' },
{ max: 255, message: '姓名长度不能大于255个字符' },
]}
name="name"
label="姓名"
/>
<ProFormText
rules={[{ required: true, message: '请输入用户名' },
{ max: 255, message: '用户名长度不能大于255个字符' },
]}
name="username"
label="用户名"
/>
<ProFormSelect
disabled={editData}
rules={[{ required: true, message: '请选择角色' }]}
options={[
{ label: '系统管理员', value: '系统管理员' },
{ label: '数据消费者', value: '数据消费者' },
]}
name="role"
label="角色"
/>
<ProFormSwitch name="enabled" label="是否启用"
fieldProps={
{ defaultChecked: true }
}
/>
</ModalForm>
);
};

61
web/client/src/sections/memberManagement/components/resetPassword.js

@ -0,0 +1,61 @@
import React, { useRef } from 'react';
import { Button, Form } from 'antd';
import {
ModalForm,
ProFormText,
} from '@ant-design/pro-form';
export default (props) => {
const { title, triggerRender, editData = null, onFinish } = props;
const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
const initialValues = {};
const [form] = Form.useForm();
const formRef = useRef();
return (
<ModalForm
formRef={formRef}
title={title || ''}
initialValues={initialValues}
trigger={
triggerRender ? triggerRender : <Button type="primary" >
{title || ''}
</Button>
}
layout="horizontal"
grid={true}
{...formItemLayout}
modalProps={{
destroyOnClose: true,
onCancel: () => { },
}}
onFinish={async (values) => {
return onFinish && await onFinish({ ...values, msg: '重置密码' }, editData, '重置密码')
}}
width={500}
>
<ProFormText.Password
rules={[{ required: true, message: '请输入旧密码' },
{ max: 255, message: '旧密码长度不能大于255个字符' },
{
pattern: /^[a-z0-9A-Z]{6,20}$/, message: '密码由6-20位字母或数字组成'
},
]}
name="oldpassword"
label="旧密码"
/>
<ProFormText.Password
rules={[{ required: true, message: '请输入新密码' },
{ max: 255, message: '新密码长度不能大于255个字符' },
{
pattern: /^[a-z0-9A-Z]{6,20}$/, message: '密码由6-20位字母或数字组成'
},
]}
name="password"
label="新密码"
/>
</ModalForm>
);
};

6
web/client/src/sections/memberManagement/components/style.less

@ -0,0 +1,6 @@
.step-footer {
display: flex;
justify-content: flex-end;
margin-top: 20px;
width: 100%;
}

26
web/client/src/sections/memberManagement/components/taskModal.js

@ -0,0 +1,26 @@
import React, { useEffect, useState } from 'react'
import { Tabs, Card, Modal } from 'antd'
import AdapterStep from './adapterStep';
import { STEP_CONFIG } from './steps/index'
function DataSourceModal(props) {
const { visible, editData, onFinish, onCancel,
type = 'postgre',//当前卡片的key (目前只有postgre,支持后续扩展)
dataSourceFilter,
} = props;
const { StepThree } = STEP_CONFIG[type];
// const onFinish = () => { }
return <>
<Modal
title={editData ? '编辑采集任务' : "新建采集任务"}
onCancel={() => { onCancel() }}
open={visible}
footer={null}
width={1200}
destroyOnClose={true}
>
<StepThree next={onFinish} dataSourceFilter={dataSourceFilter} editData={editData} />
</Modal>
</>
}
export default DataSourceModal

5
web/client/src/sections/memberManagement/containers/index.js

@ -0,0 +1,5 @@
'use strict';
import DataSourceManagement from './member';
export { DataSourceManagement };

230
web/client/src/sections/memberManagement/containers/member.js

@ -0,0 +1,230 @@
import React, { useEffect, useState } from 'react'
import { Spin, Popconfirm, Select, Row, Col, Button, Input, Table } from 'antd';
import { connect } from 'react-redux';
import ProTable from '@ant-design/pro-table';
import moment from 'moment';
import MemberModal from '../components/memberModal';
import ResetPasswordModal from '../components/resetPassword';
import { useFsRequest, ApiTable } from '$utils';
import './style.less';
function Member(props) {
const { loading, clientHeight, actions, dispatch, member, user } = props;
const [pageSize, setPageSize] = useState(10);
const [currentPage, setCurrentPage] = useState(1);
const [searchValue, setSearchValue] = useState('')
const [visible, setVisible] = useState(false);//是否展示新增编辑模态框
const [editData, setEditData] = useState(null);//模态框编辑数据
const [refreshTree, setRefreshTree] = useState(1);
const [searchRole, setSearchRole] = useState()
const queryData = (search) => {
const query = {
limit: search ? 10 : pageSize || 10,
page: search ? 1 : currentPage || 1,
name: searchValue,
role: searchRole
}
dispatch(actions.memberManagement.getUserList(query));
}
const { data: treeData = [] } = useFsRequest({
url: ApiTable.getResourceCatalog,
refreshDeps: [refreshTree]
});
useEffect(() => {
queryData();
}, [pageSize, currentPage]);
const columns = [
{
title: '序号',
dataIndex: 'index',
render: (text, record, index) => { return index + 1 }
},
{
title: '姓名',
dataIndex: 'name',
},
{
title: '用户名',
dataIndex: 'username',
},
{
title: '角色',
dataIndex: 'role',
},
{
title: '状态',
dataIndex: 'enabled',
render: (text, record) => {
return <span style={{ color: record?.enabled ? '#87d068' : '#f50' }}> {record?.enabled ? '正常' : '禁用'}</span>
}
},
{
title: '操作',
width: 160,
key: 'option',
valueType: 'option',
render: (text, record) => {
const options = [];
options.push(<MemberModal
editData={record}
triggerRender={<a>编辑</a>}
title="编辑用户"
onFinish={onFinish}
key="editUser"
/>)
options.push(
<Popconfirm
key="del"
placement="top"
title={<><div>是否确认删除该用户</div>
</>}
onConfirm={() => handleDelete(record.id)}
okText="是"
cancelText="否"
>
<a>删除</a>
</Popconfirm>)
user?.username == 'SuperAdmin' && options.push(
<Popconfirm
key="del"
placement="top"
title={<><div>是否确认重置该用户密码</div>
</>}
onConfirm={() => {
dispatch(actions.memberManagement.modifyUser(record.id, { password: 'e10adc3949ba59abbe56e057f20f883e' }, '重置密码'))
}}
okText="是"
cancelText="否"
>
<a>重置密码</a>
</Popconfirm>)
return options;
},
},
];
const handleDelete = (id) => {
dispatch(actions.memberManagement.deleteUser(id)).then(() => {
queryData();
});
};
const onFinish = async (values, editData) => {
if (editData) {
const dataToSave = { ...values }
return dispatch(
actions.memberManagement.modifyUser(editData.id, dataToSave, values?.msg || ''),
).then((res) => {
if (res.success) {
queryData();
return true;
} else {
return false;
}
});
}
return dispatch(actions.memberManagement.addUser({
...values,
})).then(res => {
if (res.success) {
queryData();
return true;
} else {
return false;
}
});
};
return <Spin spinning={loading}>
<Row className='protable-title'>
<Col span={12}>
<MemberModal
triggerRender={<Button type='primary'>新建</Button>}
title="新建用户"
onFinish={onFinish}
key="addModel"
/>
</Col>
<Col span={12} style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}>
<span>用户姓名 </span> <Input
value={searchValue} onChange={e => { setSearchValue(e.target.value) }}
style={{ width: 220, marginRight: 15 }} placeholder="请输入" />
<span>角色 </span> <Select
allowClear
value={searchRole} onChange={e => { setSearchRole(e) }}
style={{ width: 220, marginRight: 15 }} placeholder="请选择" >
<Select.Option value={'系统管理员'}>系统管理员</Select.Option>
<Select.Option value={'数据消费者'}>数据消费者</Select.Option>
</Select>
<Button onClick={() => {
setCurrentPage(1)
setPageSize(10)
queryData(true)
}} type='primary'>查询</Button></Col>
</Row>
<ProTable
columns={columns}
dateFormatter="string"
search={false}
scroll={
{
scrollToFirstRowOnChange: true,
y: clientHeight - 260
}
}
pagination={{
size: 'large',
total: member?.count,
showSizeChanger: true,
showQuickJumper: true,
current: currentPage,
pageSize: pageSize || 10,
defaultPageSize: 10,
pageSizeOptions: [10, 20, 50],
showTotal: (total) => {
return <span style={{ fontSize: 15 }}>{`${Math.ceil(total / pageSize)}页,${total}`}</span>
},
onShowSizeChange: (currentPage, pageSize) => {
setCurrentPage(currentPage);
setPageSize(pageSize);
},
onChange: (page, pageSize) => {
setCurrentPage(page);
setPageSize(pageSize);
}
}}
dataSource={member?.rows || []}
options={false}
/>
</Spin>
}
function mapStateToProps(state) {
const {
auth, global, datasources, member
} = state;
return {
loading: datasources.isRequesting,
clientHeight: global.clientHeight,
actions: global.actions,
member: member?.data || {},
user: auth.user
};
}
export default connect(mapStateToProps)(Member);

5
web/client/src/sections/memberManagement/containers/style.less

@ -0,0 +1,5 @@
.protable-title {
margin-bottom: 16px;
padding-left: 24px;
padding-right: 24px;
}

15
web/client/src/sections/memberManagement/index.js

@ -0,0 +1,15 @@
'use strict';
import reducers from './reducers';
import routes from './routes';
import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'memberManagement',
name: '用户管理',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

17
web/client/src/sections/memberManagement/nav-item.js

@ -0,0 +1,17 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Menu } from 'antd';
import { BarChartOutlined } from '@ant-design/icons';
const SubMenu = Menu.SubMenu;
export function getNavItem(user) {
return (
user?.role == '系统管理员' && <SubMenu key="memberManagement" icon={<BarChartOutlined />} title='用户管理'>
<Menu.Item key="auth">
<Link to="/memberManagement/auth">用户权限</Link>
</Menu.Item>
</ SubMenu >
)
}

5
web/client/src/sections/memberManagement/reducers/index.js

@ -0,0 +1,5 @@
'use strict';
export default {
}

17
web/client/src/sections/memberManagement/routes.js

@ -0,0 +1,17 @@
'use strict';
import { Adapter, DataSourceManagement, AcquisitionTask, AcquisitionLog, AdapterDetail } from './containers';
export default [{
type: 'inner',
route: {
path: '/memberManagement',
key: 'memberManagement',
breadcrumb: '用户管理',
// 不设置 component 则面包屑禁止跳转
childRoutes: [{
path: '/auth',
key: 'auth',
component: DataSourceManagement,
breadcrumb: '权限管理'
}]
}
}];

4
web/client/src/sections/metadataAcquisition/nav-item.js

@ -4,9 +4,9 @@ import { Menu } from 'antd';
import { BarChartOutlined } from '@ant-design/icons';
const SubMenu = Menu.SubMenu;
export function getNavItem() {
export function getNavItem(user) {
return (
<SubMenu key="metadataAcquisition" icon={<BarChartOutlined />} title='元数据采集'>
user?.role == '系统管理员' && <SubMenu key="metadataAcquisition" icon={<BarChartOutlined />} title='元数据采集'>
<Menu.Item key="Adapter">
<Link to="/metadataAcquisition/Adapter">适配器管理</Link>
</Menu.Item>

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

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Table, Popconfirm, Button, Input } from 'antd';
import { Spin, Table, Popconfirm, Button, Input, Row, Col } from 'antd';
import { ButtonGroup } from '$components';
import moment from 'moment';
import FileSaver from 'file-saver';
@ -173,13 +173,18 @@ const DatabaseTable = (props) => {
ra.applyBy == user.id && ra.resourceName === record.name);
return <ButtonGroup>
<a onClick={() => onView(record)}>查看</a>
<a style={{ marginLeft: 10 }} onClick={() => onEdit(record)}>编辑</a>
<Popconfirm
title="是否确认删除该元数据?"
onConfirm={() => confirmDelete(record.id)}
> <a style={{ marginLeft: 10 }}>删除</a></Popconfirm>
{record.type === '表' ? <a style={{ marginLeft: 10 }} onClick={() => marking(record.id)}>打标</a> : null}
{record.type === '表' ? resourceApplicationsRecords.length === 0 ?
{user.role == '数据消费者' ? null :
<>
<a style={{ marginLeft: 10 }} onClick={() => onEdit(record)}>编辑</a>
<Popconfirm
title="是否确认删除该元数据?"
onConfirm={() => confirmDelete(record.id)}
> <a style={{ marginLeft: 10 }}>删除</a></Popconfirm>
{record.type === '表' ? <a style={{ marginLeft: 10 }} onClick={() => marking(record.id)}>打标</a> : null}
</>
}
{user.role !== '数据消费者' ? null : record.type === '表' ? resourceApplicationsRecords.length === 0 ?
<a style={{ marginLeft: 10 }} onClick={() => applyResources(record)}>申请资源</a> :
<span style={{ marginLeft: 10, color: "#c0c0c0" }} title='已存在资源申请'>申请资源</span> : null}
</ButtonGroup>
@ -252,27 +257,33 @@ const DatabaseTable = (props) => {
}
}
return <Spin spinning={isRequesting}>
<div style={{ marginBottom: 16 }}>
<Button type='primary' onClick={() => {
dispatch(metadataManagement.getMetadataModels({ modelTypes: ModelTypes.join(',') })).then(res => {
if (res.success) {
setEditData({ add: true, title: '新建库表元数据', record: { path: '/' + resourceCatalogPath.join('/'), type: '目录' } });
setModalVisible(true);
<Row style={{ marginBottom: 16 }}>
<Col span={12}>
{user.role == '数据消费者' ? null : <>
<Button type='primary' onClick={() => {
dispatch(metadataManagement.getMetadataModels({ modelTypes: ModelTypes.join(',') })).then(res => {
if (res.success) {
setEditData({ add: true, title: '新建库表元数据', record: { path: '/' + resourceCatalogPath.join('/'), type: '目录' } });
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>
{
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 >
</>}
</Col>
<Col span={12} style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Input style={{ width: 220 }} placeholder="名称/代码/类型"
allowClear onPressEnter={onSearch} onChange={e => setKeywords(e.target.value || '')} />
<Button type='primary' style={{ marginLeft: 16 }} onClick={onSearch}>查询</Button>
</Col>
</Row >
<Table
scroll={{ y: clientHeight - 320 }}
rowKey='id'

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

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Table, Popconfirm, Button, Input } from 'antd';
import { Spin, Table, Popconfirm, Button, Input, Row, Col } from 'antd';
import { ButtonGroup } from '$components';
import moment from 'moment';
import FileSaver from 'file-saver';
@ -207,13 +207,18 @@ const FilesTable = (props) => {
let resourceApplicationsRecords = metadataResourceApplications.filter(ra =>
ra.applyBy == user.id && ra.resourceName === record.name);
return <ButtonGroup>
<a style={{ marginLeft: 10 }} onClick={() => onEdit(record)}>编辑</a>
<Popconfirm
title="是否确认删除该元数据?"
onConfirm={() => confirmDelete(record)}
> <a style={{ marginLeft: 10 }}>删除</a></Popconfirm>
<a style={{ marginLeft: 10 }} onClick={() => marking(record.id)}>打标</a>
{resourceApplicationsRecords.length === 0 ?
{user.role == '数据消费者' ? null :
<>
<a style={{ marginLeft: 10 }} onClick={() => onEdit(record)}>编辑</a>
<Popconfirm
title="是否确认删除该元数据?"
onConfirm={() => confirmDelete(record)}
><a style={{ marginLeft: 10 }}>删除</a></Popconfirm>
<a style={{ marginLeft: 10 }} onClick={() => marking(record.id)}>打标</a>
</>
}
{user.role == '系统管理员' ? '' : resourceApplicationsRecords.length === 0 ?
<a style={{ marginLeft: 10 }} onClick={() => applyResources(record)}>申请资源</a> :
<span style={{ marginLeft: 10, color: "#c0c0c0" }} title='已存在资源申请'>申请资源</span>}
</ButtonGroup>
@ -298,27 +303,34 @@ const FilesTable = (props) => {
}
}
return <Spin spinning={isRequesting}>
<div style={{ marginBottom: 16 }}>
<Button type='primary' onClick={() => {
dispatch(metadataManagement.getMetadataModels({ modelTypes: '文件' })).then(res => {
if (res.success) {
setEditData({ add: true, title: '新建文件元数据', record: {} });
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 >
<Row style={{ marginBottom: 16 }}>
<Col span={12}>
{user.role == '数据消费者' ? null : <>
<Button type='primary' onClick={() => {
dispatch(metadataManagement.getMetadataModels({ modelTypes: '文件' })).then(res => {
if (res.success) {
setEditData({ add: true, title: '新建文件元数据', record: {} });
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>
}</>}
</Col>
<Col span={12} style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Input style={{ width: 220 }} placeholder="名称/类型"
allowClear onPressEnter={onSearch} onChange={e => setKeywords(e.target.value || '')} />
<Button type='primary' style={{ marginLeft: 16 }} onClick={onSearch}>查询</Button>
</Col>
</Row>
<Table
scroll={{ y: clientHeight - 320 }}
rowKey='id'

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

@ -183,10 +183,10 @@ const LatestMetadata = (props) => {
<Input style={{ width: 220, marginBottom: 8 }} placeholder="输入资源目录关键字搜索"
allowClear onChange={onChangeSearch}
onKeyPress={onChangeSearch} />
<Button type='primary' style={{ marginBottom: 16 }} onClick={() => {
{user?.role == '系统管理员' && <Button type='primary' style={{ marginBottom: 16 }} onClick={() => {
setEditData({ title: '新建资源目录', add: true });
setModalVisible(true);
}}>新建资源目录</Button>
}}>新建资源目录</Button>}
<Tree
// showLine
height={clientHeight - 180}

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

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Table, Popconfirm, Button, Input } from 'antd';
import { Spin, Table, Popconfirm, Button, Input, Row, Col } from 'antd';
import { ButtonGroup } from '$components';
import MetadataRestapiModal from '../components/metadataRestapiModal';
import MetadataTagModal from '../components/metadataTagModal';
@ -143,13 +143,18 @@ const RestapisTable = (props) => {
let resourceApplicationsRecords = metadataResourceApplications.filter(ra =>
ra.applyBy == user.id && ra.resourceName === record.name);
return <ButtonGroup>
<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>
{resourceApplicationsRecords.length === 0 ?
{user.role == '数据消费者' ? null :
<>
<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>
</>
}
{user.role == '系统管理员' ? '' : resourceApplicationsRecords.length === 0 ?
<a style={{ marginLeft: 10 }} onClick={() => applyResources(record)}>申请资源</a> :
<span style={{ marginLeft: 10, color: "#c0c0c0" }} title='已存在资源申请'>申请资源</span>}
</ButtonGroup>
@ -180,19 +185,24 @@ const RestapisTable = (props) => {
}
}
return <Spin spinning={isRequesting}>
<div style={{ marginBottom: 16 }}>
<Button type='primary' onClick={() => {
dispatch(metadataManagement.getMetadataModels({ modelTypes: '接口' })).then(res => {
if (res.success) {
setEditData({ add: true, title: '新建接口元数据', record: {} });
setModalVisible(true);
}
})
}}>新建</Button>
<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 >
<Row style={{ marginBottom: 16 }}>
<Col span={12}>
{user.role == '数据消费者' ? null : <> <Button type='primary' onClick={() => {
dispatch(metadataManagement.getMetadataModels({ modelTypes: '接口' })).then(res => {
if (res.success) {
setEditData({ add: true, title: '新建接口元数据', record: {} });
setModalVisible(true);
}
})
}}>新建</Button></>}
</Col>
<Col span={12} style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Input style={{ width: 220 }} placeholder="名称"
allowClear onPressEnter={onSearch} onChange={e => setKeywords(e.target.value || '')} />
<Button type='primary' style={{ marginLeft: 16 }} onClick={onSearch}>查询</Button>
</Col>
</Row>
<Table
scroll={{ y: clientHeight - 320 }}
rowKey='id'

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

@ -76,7 +76,11 @@ export const ApiTable = {
getLogs: "meta/acq/logs",
//资源消费
approveList:'resource/approve'
approveList:'resource/approve',
//用户管理
getUserList: 'meta/members',
addUser: 'meta/member',
modifyUser: 'meta/member/{id}',
};
export const RouteTable = {

Loading…
Cancel
Save