diff --git a/web/client/src/app.js b/web/client/src/app.js
index f45bedc..60017ea 100644
--- a/web/client/src/app.js
+++ b/web/client/src/app.js
@@ -11,6 +11,7 @@ import resourceRetrieval from './sections/resourceRetrieval';
import memberManagement from './sections/memberManagement';
import dataQuality from './sections/dataQuality';
import safetySpecification from './sections/safetySpecification';
+import backups from './sections/backups';
const App = props => {
const { projectName } = props
@@ -31,6 +32,7 @@ const App = props => {
dataQuality,
safetySpecification,
memberManagement,
+ backups
]}
/>
)
diff --git a/web/client/src/sections/backups/actions/index.js b/web/client/src/sections/backups/actions/index.js
new file mode 100644
index 0000000..5abd7d2
--- /dev/null
+++ b/web/client/src/sections/backups/actions/index.js
@@ -0,0 +1,6 @@
+'use strict';
+
+import * as member from './member';
+export default {
+ ...member
+}
\ No newline at end of file
diff --git a/web/client/src/sections/backups/actions/member.js b/web/client/src/sections/backups/actions/member.js
new file mode 100644
index 0000000..ec6ab27
--- /dev/null
+++ b/web/client/src/sections/backups/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 || '用户编辑',
+ },
+ });
+}
+
diff --git a/web/client/src/sections/backups/actions/task.js b/web/client/src/sections/backups/actions/task.js
new file mode 100644
index 0000000..11d8ce3
--- /dev/null
+++ b/web/client/src/sections/backups/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 || '任务执行',
+ },
+ });
+}
+
+
diff --git a/web/client/src/sections/backups/components/memberModal.js b/web/client/src/sections/backups/components/memberModal.js
new file mode 100644
index 0000000..e2e7a26
--- /dev/null
+++ b/web/client/src/sections/backups/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 (
+
+ {title || ''}
+
+ }
+ layout="horizontal"
+ grid={true}
+ {...formItemLayout}
+ modalProps={{
+ destroyOnClose: true,
+ onCancel: () => { },
+ }}
+ onFinish={async (values) => {
+ return onFinish && await onFinish(values, editData, form)
+ // return true;
+ }}
+ width={500}
+ >
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/web/client/src/sections/backups/components/resetPassword.js b/web/client/src/sections/backups/components/resetPassword.js
new file mode 100644
index 0000000..f633c36
--- /dev/null
+++ b/web/client/src/sections/backups/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 (
+
+ {title || ''}
+
+ }
+ layout="horizontal"
+ grid={true}
+ {...formItemLayout}
+ modalProps={{
+ destroyOnClose: true,
+ onCancel: () => { },
+ }}
+ onFinish={async (values) => {
+ return onFinish && await onFinish({ ...values, msg: '重置密码' }, editData, '重置密码')
+ }}
+ width={500}
+ >
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/web/client/src/sections/backups/components/style.less b/web/client/src/sections/backups/components/style.less
new file mode 100644
index 0000000..cf451f1
--- /dev/null
+++ b/web/client/src/sections/backups/components/style.less
@@ -0,0 +1,6 @@
+.step-footer {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 20px;
+ width: 100%;
+}
\ No newline at end of file
diff --git a/web/client/src/sections/backups/components/taskModal.js b/web/client/src/sections/backups/components/taskModal.js
new file mode 100644
index 0000000..0278fa8
--- /dev/null
+++ b/web/client/src/sections/backups/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 <>
+ { onCancel() }}
+ open={visible}
+ footer={null}
+ width={1200}
+ destroyOnClose={true}
+ >
+
+
+ >
+}
+
+export default DataSourceModal
\ No newline at end of file
diff --git a/web/client/src/sections/backups/containers/index.js b/web/client/src/sections/backups/containers/index.js
new file mode 100644
index 0000000..779d542
--- /dev/null
+++ b/web/client/src/sections/backups/containers/index.js
@@ -0,0 +1,5 @@
+'use strict';
+
+import Restore from './restore';
+
+export { Restore };
diff --git a/web/client/src/sections/backups/containers/restore.js b/web/client/src/sections/backups/containers/restore.js
new file mode 100644
index 0000000..821ea7e
--- /dev/null
+++ b/web/client/src/sections/backups/containers/restore.js
@@ -0,0 +1,229 @@
+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 ● {record?.enabled ? '正常' : '禁用'}
+ }
+ },
+ {
+ title: '操作',
+ width: 160,
+ key: 'option',
+ valueType: 'option',
+ render: (text, record) => {
+ const options = [];
+ options.push(编辑}
+ title="编辑用户"
+ onFinish={onFinish}
+ key="editUser"
+ />)
+ options.push(
+ 是否确认删除该用户?
+ >}
+ onConfirm={() => handleDelete(record.id)}
+ okText="是"
+ cancelText="否"
+ >
+ 删除
+ )
+ user?.username == 'SuperAdmin' && options.push(
+ 是否确认重置该用户密码?
+ >}
+ onConfirm={() => {
+ dispatch(actions.memberManagement.modifyUser(record.id, { password: 'e10adc3949ba59abbe56e057f20f883e' }, '重置密码'))
+ }}
+ okText="是"
+ cancelText="否"
+ >
+ 重置密码
+ )
+
+
+ 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
+
+
+ 新建}
+ title="新建用户"
+ onFinish={onFinish}
+ key="addModel"
+ />
+
+
+ 用户姓名: { setSearchValue(e.target.value) }}
+ style={{ width: 220, marginRight: 15 }} placeholder="请输入" />
+
+ 角色:
+
+
+
+
+ {
+ return {`共${Math.ceil(total / pageSize)}页,${total}项`}
+ },
+ onShowSizeChange: (currentPage, pageSize) => {
+ setCurrentPage(currentPage);
+ setPageSize(pageSize);
+
+ },
+ onChange: (page, pageSize) => {
+ setCurrentPage(page);
+ setPageSize(pageSize);
+ }
+ }}
+ dataSource={member?.rows || []}
+ options={false}
+ />
+
+
+}
+
+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);
+
+
+
diff --git a/web/client/src/sections/backups/containers/style.less b/web/client/src/sections/backups/containers/style.less
new file mode 100644
index 0000000..8219c4f
--- /dev/null
+++ b/web/client/src/sections/backups/containers/style.less
@@ -0,0 +1,5 @@
+.protable-title {
+ margin-bottom: 16px;
+ padding-left: 24px;
+ padding-right: 24px;
+}
\ No newline at end of file
diff --git a/web/client/src/sections/backups/index.js b/web/client/src/sections/backups/index.js
new file mode 100644
index 0000000..0854c67
--- /dev/null
+++ b/web/client/src/sections/backups/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: 'backups',
+ name: '数据备份恢复',
+ reducers: reducers,
+ routes: routes,
+ actions: actions,
+ getNavItem: getNavItem
+};
\ No newline at end of file
diff --git a/web/client/src/sections/backups/nav-item.js b/web/client/src/sections/backups/nav-item.js
new file mode 100644
index 0000000..752f8dc
--- /dev/null
+++ b/web/client/src/sections/backups/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 == '系统管理员' && } title='数据备份恢复'>
+
+ 备份恢复
+
+ SubMenu >
+ )
+
+}
\ No newline at end of file
diff --git a/web/client/src/sections/backups/reducers/index.js b/web/client/src/sections/backups/reducers/index.js
new file mode 100644
index 0000000..7ed1088
--- /dev/null
+++ b/web/client/src/sections/backups/reducers/index.js
@@ -0,0 +1,5 @@
+'use strict';
+
+export default {
+
+}
\ No newline at end of file
diff --git a/web/client/src/sections/backups/routes.js b/web/client/src/sections/backups/routes.js
new file mode 100644
index 0000000..935dda3
--- /dev/null
+++ b/web/client/src/sections/backups/routes.js
@@ -0,0 +1,18 @@
+'use strict';
+import { Restore } from './containers';
+export default [{
+ type: 'inner',
+ route: {
+ path: '/backups',
+ key: 'backups',
+ breadcrumb: '用户管理',
+ // 不设置 component 则面包屑禁止跳转
+ childRoutes: [{
+ path: '/restore',
+ key: 'restore',
+ component: Restore,
+ breadcrumb: '备份恢复'
+ }]
+ }
+}];
+
diff --git a/web/client/src/sections/memberManagement/containers/index.js b/web/client/src/sections/memberManagement/containers/index.js
index 8b9e540..b322ad1 100644
--- a/web/client/src/sections/memberManagement/containers/index.js
+++ b/web/client/src/sections/memberManagement/containers/index.js
@@ -1,5 +1,5 @@
'use strict';
-import DataSourceManagement from './member';
+import MemberManagement from './member';
-export { DataSourceManagement };
+export { MemberManagement };
diff --git a/web/client/src/sections/memberManagement/routes.js b/web/client/src/sections/memberManagement/routes.js
index 0888966..91e3250 100644
--- a/web/client/src/sections/memberManagement/routes.js
+++ b/web/client/src/sections/memberManagement/routes.js
@@ -1,5 +1,5 @@
'use strict';
-import { Adapter, DataSourceManagement, AcquisitionTask, AcquisitionLog, AdapterDetail } from './containers';
+import { MemberManagement } from './containers';
export default [{
type: 'inner',
route: {
@@ -10,7 +10,7 @@ export default [{
childRoutes: [{
path: '/auth',
key: 'auth',
- component: DataSourceManagement,
+ component: MemberManagement,
breadcrumb: '权限管理'
}]
}