feat: user management i18n

This commit is contained in:
LoadChange 2020-01-07 16:45:18 +08:00
parent a43f337a5e
commit 314e7b8cb2
13 changed files with 351 additions and 84 deletions

View File

@ -29,3 +29,5 @@ export const SIGN_IN = 'SIGN_IN';
export const USER_LIST = 'USER_LIST';
export const ROLE_LIST = 'ROLE_LIST';
export const PERMISSIONS_LIST = 'PERMISSIONS_LIST';

View File

@ -508,6 +508,32 @@ const I18N_CONF = {
update: 'Update',
insert: 'Insert',
},
UserManagement: {
userManagement: 'User Management',
createUser: 'Create user',
resetPassword: 'Edit',
deleteUser: 'Delete',
deleteUserTip: 'Do you want to delete this user?',
username: 'Username',
password: 'Password',
operation: 'Operation',
},
NewUser: {
createUser: 'Create user',
username: 'Username',
password: 'Password',
usernamePlaceholder: 'Please Enter Username',
passwordPlaceholder: 'Please Enter Password',
usernameError: 'User name cannot be empty!',
passwordError: 'Password cannot be empty!',
},
PasswordReset: {
resetPassword: 'Password Reset',
username: 'Username',
password: 'Password',
passwordPlaceholder: 'Please Enter Password',
passwordError: 'Password cannot be empty!',
},
};
export default I18N_CONF;

View File

@ -505,6 +505,32 @@ const I18N_CONF = {
update: '更新',
insert: '插入',
},
UserManagement: {
userManagement: '用户管理',
createUser: '创建用户',
resetPassword: '修改',
deleteUser: '删除',
deleteUserTip: '是否要删除该用户',
username: '用户名',
password: '密码',
operation: '操作',
},
NewUser: {
createUser: '创建用户',
username: '用户名',
password: '密码',
usernamePlaceholder: '请输入用户名',
passwordPlaceholder: '请输入密码',
usernameError: '用户名不能为空',
passwordError: '密码不能为空!',
},
PasswordReset: {
resetPassword: '密码重置',
username: '用户名',
password: '密码',
passwordError: '密码不能为空',
passwordPlaceholder: '请输入密码',
},
};
export default I18N_CONF;

View File

@ -0,0 +1,90 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { Field, Form, Input, Dialog, ConfigProvider } from '@alifd/next';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: { fixedSpan: 3 },
wrapperCol: { span: 20 },
};
@ConfigProvider.config
class NewPermissions extends React.Component {
static displayName = 'NewPermissions';
field = new Field(this);
static propTypes = {
locale: PropTypes.object,
visible: PropTypes.bool,
};
check() {
const errors = {
role: '角色不能为空!',
resource: '资源不能为空!',
action: '动作不能为空!',
};
const vals = Object.keys(errors).map(key => {
const val = this.field.getValue(key);
if (!val) {
this.field.setError(key, errors[key]);
}
return val;
});
if (vals.filter(v => v).length === 3) {
return vals;
}
return null;
}
render() {
const { getError } = this.field;
const { visible, onOk, onCancel } = this.props;
return (
<>
<Dialog
title="添加授权"
visible={visible}
onOk={() => {
const vals = this.check();
if (vals) {
onOk(vals).then(() => onCancel());
}
}}
onClose={onCancel}
onCancel={onCancel}
afterClose={() => this.field.reset()}
>
<Form style={{ width: 400 }} {...formItemLayout} field={this.field}>
<FormItem label="角色" required help={getError('role')}>
<Input name="role" trim placeholder="Please Enter Role" />
</FormItem>
<FormItem label="资源" required help={getError('resource')}>
<Input name="resource" trim placeholder="Please Enter Resource" />
</FormItem>
<FormItem label="动作" required help={getError('username')}>
<Input name="action" trim placeholder="Please Enter Action" />
</FormItem>
</Form>
</Dialog>
</>
);
}
}
export default NewPermissions;

View File

@ -13,32 +13,113 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Button,
Field,
Form,
Grid,
Input,
Loading,
Pagination,
Table,
ConfigProvider,
} from '@alifd/next';
import { Button, Dialog, Pagination, Table, ConfigProvider } from '@alifd/next';
import { connect } from 'react-redux';
import { getPermissions, createPermission, deletePermission } from '../../../reducers/authority';
import RegionGroup from '../../../components/RegionGroup';
import NewPermissions from './NewPermissions';
import './PermissionsManagement.scss';
@connect(state => ({ permissions: state.authority.permissions }), { getPermissions })
@ConfigProvider.config
class PermissionsManagement extends React.Component {
static displayName = 'UserManagement';
static displayName = 'PermissionsManagement';
static propTypes = {
locale: PropTypes.object,
permissions: PropTypes.object,
getPermissions: PropTypes.func,
};
constructor(props) {
super(props);
this.state = {
loading: true,
pageNo: 1,
pageSize: 9,
createPermission: false,
};
}
componentDidMount() {
this.getPermissions();
}
getPermissions() {
const { pageNo, pageSize } = this.state;
this.props
.getPermissions({ pageNo, pageSize })
.then(() => {
if (this.state.loading) {
this.setState({ loading: false });
}
})
.catch(() => this.setState({ loading: false }));
}
colseCreatePermission() {
this.setState({ createPermissionVisible: false });
}
render() {
const { permissions } = this.props;
const { loading, pageSize, pageNo, createPermissionVisible } = this.state;
return (
<>
<h1>PermissionsManagement</h1>
<RegionGroup left={'权限管理'} />
<div className="filter-panel">
<Button type="primary" onClick={() => this.setState({ createPermissionVisible: true })}>
添加权限
</Button>
</div>
<Table dataSource={permissions.pageItems} loading={loading} maxBodyHeight={476} fixedHeader>
<Table.Column title="角色" dataIndex="role" />
<Table.Column title="资源" dataIndex="resource" />
<Table.Column title="动作" dataIndex="action" />
<Table.Column
title="操作"
cell={(value, index, record) => (
<>
<Button
type="primary"
warning
onClick={() =>
Dialog.confirm({
title: '确认',
content: '是否要删除该权限',
onOk: () =>
deletePermission(record).then(() =>
this.setState({ pageNo: 1 }, () => this.getPermissions())
),
})
}
>
刪除
</Button>
</>
)}
/>
</Table>
{permissions.totalCount > pageSize && (
<Pagination
className="users-pagination"
current={pageNo}
total={permissions.totalCount}
pageSize={pageSize}
onChange={pageNo => this.setState({ pageNo }, () => this.getPermissions())}
/>
)}
<NewPermissions
visible={createPermissionVisible}
onOk={permission =>
createPermission(permission).then(res => {
this.setState({ pageNo: 1 }, () => this.getPermissions());
return res;
})
}
onCancel={() => this.colseCreatePermission()}
/>
</>
);
}

View File

@ -68,11 +68,7 @@ class RolesManagement extends React.Component {
<>
<RegionGroup left={'角色管理'} />
<div className="filter-panel">
<Button
type="primary"
className="create-user-btn"
onClick={() => this.setState({ createRoleVisible: true })}
>
<Button type="primary" onClick={() => this.setState({ createRoleVisible: true })}>
绑定角色
</Button>
</div>

View File

@ -19,8 +19,8 @@ import './UserManagement.scss';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: { fixedSpan: 3 },
wrapperCol: { span: 20 },
labelCol: { fixedSpan: 4 },
wrapperCol: { span: 19 },
};
@ConfigProvider.config
@ -35,11 +35,12 @@ class NewUser extends React.Component {
};
check() {
const { locale } = this.props;
const errors = {
username: '用户名不能为空!',
password: '密码不能为空!',
username: locale.usernameError,
password: locale.passwordError,
};
const vals = ['username', 'password'].map(key => {
const vals = Object.keys(errors).map(key => {
const val = this.field.getValue(key);
if (!val) {
this.field.setError(key, errors[key]);
@ -53,12 +54,13 @@ class NewUser extends React.Component {
}
render() {
const { locale } = this.props;
const { getError } = this.field;
const { visible, onOk, onCancel } = this.props;
return (
<>
<Dialog
title="创建用户"
title={locale.createUser}
visible={visible}
onOk={() => {
const vals = this.check();
@ -71,11 +73,11 @@ class NewUser extends React.Component {
afterClose={() => this.field.reset()}
>
<Form style={{ width: 400 }} {...formItemLayout} field={this.field}>
<FormItem label="用户名" required help={getError('username')}>
<Input name="username" trim placeholder="Please Enter Username" />
<FormItem label={locale.username} required help={getError('username')}>
<Input name="username" trim placeholder={locale.usernamePlaceholder} />
</FormItem>
<FormItem label="密码" required help={getError('password')}>
<Input name="password" htmlType="password" placeholder="Please Enter Password" />
<FormItem label={locale.password} required help={getError('password')}>
<Input name="password" htmlType="password" placeholder={locale.passwordPlaceholder} />
</FormItem>
</Form>
</Dialog>

View File

@ -19,8 +19,8 @@ import './UserManagement.scss';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: { fixedSpan: 3 },
wrapperCol: { span: 20 },
labelCol: { fixedSpan: 4 },
wrapperCol: { span: 19 },
};
@ConfigProvider.config
@ -35,8 +35,9 @@ class PasswordReset extends React.Component {
};
check() {
const errors = { password: '密码不能为空!' };
const vals = ['password'].map(key => {
const { locale } = this.props;
const errors = { password: locale.passwordError };
const vals = Object.keys(errors).map(key => {
const val = this.field.getValue(key);
if (!val) {
this.field.setError(key, errors[key]);
@ -50,12 +51,13 @@ class PasswordReset extends React.Component {
}
render() {
const { locale } = this.props;
const { getError } = this.field;
const { username, onOk, onCancel } = this.props;
return (
<>
<Dialog
title="密码重置"
title={locale.resetPassword}
visible={username}
onOk={() => {
const vals = this.check();
@ -68,11 +70,11 @@ class PasswordReset extends React.Component {
afterClose={() => this.field.reset()}
>
<Form style={{ width: 400 }} {...formItemLayout} field={this.field}>
<FormItem label="用户名" required>
<FormItem label={locale.username} required>
<p>{username}</p>
</FormItem>
<FormItem label="密码" required help={getError('password')}>
<Input name="password" htmlType="password" placeholder="Please Enter Password" />
<FormItem label={locale.password} required help={getError('password')}>
<Input name="password" htmlType="password" placeholder={locale.passwordPlaceholder} />
</FormItem>
</Form>
</Dialog>

View File

@ -64,29 +64,25 @@ class UserManagement extends React.Component {
}
render() {
const { users } = this.props;
const { users, locale } = this.props;
const { loading, pageSize, pageNo, createUserVisible, passwordResetUser } = this.state;
return (
<>
<RegionGroup left={'用户管理'} />
<RegionGroup left={locale.userManagement} />
<div className="filter-panel">
<Button
type="primary"
className="create-user-btn"
onClick={() => this.setState({ createUserVisible: true })}
>
创建用户
<Button type="primary" onClick={() => this.setState({ createUserVisible: true })}>
{locale.createUser}
</Button>
</div>
<Table dataSource={users.pageItems} loading={loading} maxBodyHeight={476} fixedHeader>
<Table.Column title="用户名" dataIndex="username" />
<Table.Column title={locale.username} dataIndex="username" />
<Table.Column
title="密码"
title={locale.password}
dataIndex="password"
cell={value => value.replace(/\S/g, '*')}
/>
<Table.Column
title="操作"
title={locale.operation}
dataIndex="username"
cell={username => (
<>
@ -94,7 +90,7 @@ class UserManagement extends React.Component {
type="primary"
onClick={() => this.setState({ passwordResetUser: username })}
>
修改
{locale.resetPassword}
</Button>
&nbsp;&nbsp;&nbsp;
<Button
@ -102,8 +98,8 @@ class UserManagement extends React.Component {
warning
onClick={() =>
Dialog.confirm({
title: '确认',
content: '是否要删除该用户',
title: locale.deleteUser,
content: locale.deleteUserTip,
onOk: () =>
deleteUser(username).then(() =>
this.setState({ pageNo: 1 }, () => this.getUsers())
@ -111,7 +107,7 @@ class UserManagement extends React.Component {
})
}
>
刪除
{locale.deleteUser}
</Button>
</>
)}

View File

@ -4,8 +4,8 @@ import { withRouter } from 'react-router-dom';
import './index.scss';
import Header from '../../layouts/Header';
import { request } from '../../globalLib';
import PropTypes from 'prop-types';
import { login } from '../../reducers/base';
const FormItem = Form.Item;
@ -30,29 +30,16 @@ class Login extends React.Component {
if (errors) {
return;
}
request({
type: 'post',
url: 'v1/auth/login',
data: values,
success: ({ code, data }) => {
if (code === 200) {
// TODO: token
localStorage.setItem('token', data);
// TODO: 使react router
this.props.history.push('/');
}
if (code === 401) {
Message.error({
content: locale.invalidUsernameOrPassword,
});
}
},
error: () => {
login(values)
.then(res => {
localStorage.setItem('token', JSON.stringify(res));
this.props.history.push('/');
})
.catch(() =>
Message.error({
content: locale.invalidUsernameOrPassword,
});
},
});
})
);
});
};

View File

@ -13,7 +13,7 @@
import { Message } from '@alifd/next';
import request from '../utils/request';
import { UPDATE_USER, SIGN_IN, USER_LIST, ROLE_LIST } from '../constants';
import { UPDATE_USER, SIGN_IN, USER_LIST, ROLE_LIST, PERMISSIONS_LIST } from '../constants';
const initialState = {
users: {
@ -22,7 +22,18 @@ const initialState = {
pagesAvailable: 1,
pageItems: [],
},
roles: {},
roles: {
totalCount: 0,
pageNumber: 1,
pagesAvailable: 1,
pageItems: [],
},
permissions: {
totalCount: 0,
pageNumber: 1,
pagesAvailable: 1,
pageItems: [],
},
};
const successMsg = res => {
@ -82,15 +93,51 @@ const createRole = ([role, username]) =>
const deleteRole = role =>
request.delete('v1/auth/roles', { params: role }).then(res => successMsg(res));
/**
* 权限列表
* @param {*} params
*/
const getPermissions = params => dispatch =>
request
.get('v1/auth/permissions', { params })
.then(data => dispatch({ type: PERMISSIONS_LIST, data }));
/**
* 给角色添加权限
* @param {*} param0
*/
const createPermission = ([role, resource, action]) =>
request.post('v1/auth/permissions', { role, resource, action }).then(res => successMsg(res));
/**
* 删除权限
* @param {*} param0
*/
const deletePermission = permission =>
request.delete('v1/auth/permissions', { params: permission }).then(res => successMsg(res));
export default (state = initialState, action) => {
switch (action.type) {
case USER_LIST:
return { ...state, users: { ...action.data } };
case ROLE_LIST:
return { ...state, roles: { ...action.data } };
case PERMISSIONS_LIST:
return { ...state, permissions: { ...action.data } };
default:
return state;
}
};
export { getUsers, createUser, deleteUser, passwordReset, getRoles, createRole, deleteRole };
export {
getUsers,
createUser,
deleteUser,
passwordReset,
getRoles,
createRole,
deleteRole,
getPermissions,
createPermission,
deletePermission,
};

View File

@ -20,6 +20,12 @@ const initialState = {
functionMode: '',
};
/**
* 用户登录
* @param {*} param0
*/
const login = user => request.post('v1/auth/users/login', user);
const getState = () => dispatch =>
request
.get('v1/console/server/state')
@ -52,4 +58,4 @@ export default (state = initialState, action) => {
}
};
export { getState };
export { getState, login };

View File

@ -9,18 +9,24 @@ const request = () => {
const instance = axios.create();
instance.interceptors.request.use(
function(config) {
config => {
if (!config.params) {
config.params = {};
}
if (!config.url.includes('auth/users/login')) {
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
config.params.accessToken = accessToken;
}
if (['post', 'put'].includes(config.method)) {
config.data = qs.stringify(config.data);
config.headers = {
'Content-Type': 'application/x-www-form-urlencoded',
};
if (!config.headers) {
config.headers = {};
}
config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
return config;
},
function(error) {
return Promise.reject(error);
}
error => Promise.reject(error)
);
instance.interceptors.response.use(