🎉 增加多终端令牌认证

This commit is contained in:
smallchill 2019-04-01 23:01:21 +08:00
parent 0c3b807ea4
commit 99e6dfee83
32 changed files with 1024 additions and 108 deletions

View File

@ -1,5 +1,6 @@
## 简介
Sword 是 [SpringBlade](https://gitee.com/smallc/SpringBlade)前端UI项目基于react 、ant design、dva用于快速构建系统中后台业务。
* Sword 是 [SpringBlade](https://gitee.com/smallc/SpringBlade)前端UI项目基于react 、ant design、dva用于快速构建系统中后台业务。
* Sword 已入选 ant design 官方脚手架市场,地址:[scaffolds.ant.design](http://scaffold.ant.design/#/scaffolds/Sword)
## 文档
* 文档地址:[Sword开发手册](https://www.kancloud.cn/smallchill/sword)

View File

@ -166,6 +166,16 @@ export default [
{ path: '/system/tenant/view/:id', component: './System/Tenant/TenantView' },
],
},
{
path: '/system/client',
routes: [
{ path: '/system/client', redirect: '/system/client/list' },
{ path: '/system/client/list', component: './System/Client/Client' },
{ path: '/system/client/add', component: './System/Client/ClientAdd' },
{ path: '/system/client/edit/:id', component: './System/Client/ClientEdit' },
{ path: '/system/client/view/:id', component: './System/Client/ClientView' },
],
},
],
},
{

65
mock/client.js Normal file
View File

@ -0,0 +1,65 @@
import { delay } from 'roadhog-api-doc';
function getFakeList(req, res) {
const json = { code: 200, success: true, msg: '操作成功' };
const list = [];
list.push(
{
id: '1',
clientId: 'sword',
clientSecret: 'sword_secret',
scope: 'all',
authorizedGrantTypes: 'refresh_token,password,authorization_code',
webServerRedirectUri: 'https://sword.bladex.vip',
accessTokenValidity: '3600',
refreshTokenValidity: '36000',
},
{
id: '2',
clientId: 'saber',
clientSecret: 'saber_secret',
scope: 'all',
authorizedGrantTypes: 'refresh_token,password,authorization_code',
webServerRedirectUri: 'https://saber.bladex.vip',
accessTokenValidity: '3600',
refreshTokenValidity: '36000',
}
);
json.data = {
total: 10,
size: 10,
current: 2,
searchCount: true,
pages: 1,
records: list,
};
return res.json(json);
}
function getFakeDetail(req, res) {
const json = { code: 200, success: true, msg: '操作成功' };
json.data = {
id: '1',
clientId: 'sword',
clientSecret: 'sword_secret',
scope: 'all',
authorizedGrantTypes: 'refresh_token,password,authorization_code',
webServerRedirectUri: 'https://sword.bladex.vip',
accessTokenValidity: '3600',
refreshTokenValidity: '36000',
};
return res.json(json);
}
function fakeSuccess(req, res) {
const json = { code: 200, success: true, msg: '操作成功' };
return res.json(json);
}
const proxy = {
'GET /api/blade-system/client/list': getFakeList,
'GET /api/blade-system/client/detail': getFakeDetail,
'POST /api/blade-system/client/submit': fakeSuccess,
'POST /api/blade-system/client/remove': fakeSuccess,
};
export default delay(proxy, 500);

View File

@ -59,7 +59,8 @@
"react-fittext": "^1.0.0",
"react-media": "^1.9.2",
"react-router-dom": "^4.3.1",
"roadhog-api-doc": "^1.1.2"
"roadhog-api-doc": "^1.1.2",
"js-base64": "^2.5.1"
},
"devDependencies": {
"@types/react": "^16.8.1",

36
src/actions/client.js Normal file
View File

@ -0,0 +1,36 @@
export const CLIENT_NAMESPACE = 'client';
export function CLIENT_LIST(payload) {
return {
type: `${CLIENT_NAMESPACE}/fetchList`,
payload,
};
}
export function CLIENT_DETAIL(id) {
return {
type: `${CLIENT_NAMESPACE}/fetchDetail`,
payload: { id },
};
}
export function CLIENT_CLEAR_DETAIL() {
return {
type: `${CLIENT_NAMESPACE}/clearDetail`,
payload: {},
};
}
export function CLIENT_SUBMIT(payload) {
return {
type: `${CLIENT_NAMESPACE}/submit`,
payload,
};
}
export function CLIENT_REMOVE(payload) {
return {
type: `${CLIENT_NAMESPACE}/remove`,
payload,
};
}

View File

@ -1,4 +1,8 @@
module.exports = {
title: 'Sword企业级开发平台',
clientId: 'sword', // 客户端id
clientSecret: 'sword_secret', // 客户端密钥
tenantMode: true, // 开启租户模式
navTheme: 'dark', // theme for nav menu
primaryColor: '#1890FF', // primary color of ant design
layout: 'sidemenu', // nav menu position: sidemenu or topmenu
@ -10,6 +14,5 @@ module.exports = {
menu: {
disableLocal: false,
},
title: 'Sword企业级开发平台',
pwa: true,
};

View File

@ -14,6 +14,7 @@ export default {
'menu.system.role': 'role',
'menu.system.param': 'parameter',
'menu.system.tenant': 'tenant',
'menu.system.client': 'client',
'menu.monitor': 'monitor',
'menu.monitor.log': 'log',
'menu.monitor.log.log_usual': 'usual log',

View File

@ -14,6 +14,7 @@ export default {
'menu.system.role': '角色管理',
'menu.system.param': '参数管理',
'menu.system.tenant': '租户管理',
'menu.system.client': '应用管理',
'menu.monitor': '系统监控',
'menu.monitor.log': '日志管理',
'menu.monitor.log.log_usual': '通用日志',

View File

@ -14,6 +14,7 @@ export default {
'menu.system.role': '角色管理',
'menu.system.param': '參數管理',
'menu.system.tenant': '租戶管理',
'menu.system.client': '應用管理',
'menu.monitor': '系統監控',
'menu.monitor.log': '日志管理',
'menu.monitor.log.log_usual': '通用日志',

87
src/models/client.js Normal file
View File

@ -0,0 +1,87 @@
import { message } from 'antd';
import router from 'umi/router';
import { CLIENT_NAMESPACE } from '../actions/client';
import { list, submit, detail, remove } from '../services/client';
export default {
namespace: CLIENT_NAMESPACE,
state: {
data: {
list: [],
pagination: false,
},
detail: {},
},
effects: {
*fetchList({ payload }, { call, put }) {
const response = yield call(list, payload);
if (response.success) {
yield put({
type: 'saveList',
payload: {
list: response.data.records,
pagination: {
total: response.data.total,
current: response.data.current,
pageSize: response.data.size,
},
},
});
}
},
*fetchDetail({ payload }, { call, put }) {
const response = yield call(detail, payload);
if (response.success) {
yield put({
type: 'saveDetail',
payload: {
detail: response.data,
},
});
}
},
*clearDetail({ payload }, { put }) {
yield put({
type: 'removeDetail',
payload: { payload },
});
},
*submit({ payload }, { call }) {
const response = yield call(submit, payload);
if (response.success) {
message.success('提交成功');
router.push('/system/client');
}
},
*remove({ payload }, { call }) {
const {
data: { keys },
success,
} = payload;
const response = yield call(remove, { ids: keys });
if (response.success) {
success();
}
},
},
reducers: {
saveList(state, action) {
return {
...state,
data: action.payload,
};
},
saveDetail(state, action) {
return {
...state,
detail: action.payload.detail,
};
},
removeDetail(state) {
return {
...state,
detail: {},
};
},
},
};

View File

@ -4,6 +4,7 @@ import { formatMessage, FormattedMessage } from 'umi/locale';
import { Checkbox, Alert } from 'antd';
import Login from '../../components/Login';
import styles from './Login.less';
import { tenantMode } from '../../defaultSettings';
const { Tab, TenantCode, UserName, Password, Submit } = Login;
@ -80,16 +81,18 @@ class LoginPage extends Component {
login.type === 'account' &&
!submitting &&
this.renderMessage(formatMessage({ id: 'app.login.message-invalid-credentials' }))}
<TenantCode
name="tenantCode"
placeholder={`${formatMessage({ id: 'app.login.tenantCode' })}: 000000`}
rules={[
{
required: true,
message: formatMessage({ id: 'validation.tenantCode.required' }),
},
]}
/>
{tenantMode ? (
<TenantCode
name="tenantCode"
placeholder={`${formatMessage({ id: 'app.login.tenantCode' })}: 000000`}
rules={[
{
required: true,
message: formatMessage({ id: 'validation.tenantCode.required' }),
},
]}
/>
) : null}
<UserName
name="account"
placeholder={`${formatMessage({ id: 'app.login.userName' })}: admin`}

View File

@ -0,0 +1,99 @@
import React, { PureComponent } from 'react';
import { connect } from 'dva';
import { Button, Col, Form, Input, Row } from 'antd';
import Panel from '../../../components/Panel';
import { CLIENT_LIST } from '../../../actions/client';
import Grid from '../../../components/Sword/Grid';
const FormItem = Form.Item;
@connect(({ client, loading }) => ({
client,
loading: loading.models.client,
}))
@Form.create()
class Client extends PureComponent {
// ============ 查询 ===============
handleSearch = params => {
const { dispatch } = this.props;
dispatch(CLIENT_LIST(params));
};
// ============ 查询表单 ===============
renderSearchForm = onReset => {
const { form } = this.props;
const { getFieldDecorator } = form;
return (
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={6} sm={24}>
<FormItem label="客户端id">
{getFieldDecorator('clientId')(<Input placeholder="请输入客户端id" />)}
</FormItem>
</Col>
<Col>
<div style={{ float: 'right' }}>
<Button type="primary" htmlType="submit">
查询
</Button>
<Button style={{ marginLeft: 8 }} onClick={onReset}>
重置
</Button>
</div>
</Col>
</Row>
);
};
render() {
const code = 'client';
const {
form,
loading,
client: { data },
} = this.props;
const columns = [
{
title: '客户端id',
dataIndex: 'clientId',
},
{
title: '客户端密钥',
dataIndex: 'clientSecret',
},
{
title: '授权范围',
dataIndex: 'scope',
},
{
title: '授权类型',
dataIndex: 'authorizedGrantTypes',
},
{
title: '回调地址',
dataIndex: 'webServerRedirectUri',
},
{
title: '令牌过期秒数',
dataIndex: 'accessTokenValidity',
},
];
return (
<Panel>
<Grid
code={code}
form={form}
onSearch={this.handleSearch}
renderSearchForm={this.renderSearchForm}
loading={loading}
data={data}
columns={columns}
/>
</Panel>
);
}
}
export default Client;

View File

@ -0,0 +1,188 @@
import React, { PureComponent } from 'react';
import { Form, Input, Card, Button, Row, Col, InputNumber } from 'antd';
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
import { CLIENT_SUBMIT } from '../../../actions/client';
const FormItem = Form.Item;
@connect(({ loading }) => ({
submitting: loading.effects['client/submit'],
}))
@Form.create()
class ClientAdd extends PureComponent {
handleSubmit = e => {
e.preventDefault();
const { dispatch, form } = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
dispatch(CLIENT_SUBMIT(values));
}
});
};
render() {
const {
form: { getFieldDecorator },
submitting,
} = this.props;
const formItemLayout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
const formAllItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
},
};
const action = (
<Button type="primary" onClick={this.handleSubmit} loading={submitting}>
提交
</Button>
);
return (
<Panel title="新增" back="/system/client" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="客户端id">
{getFieldDecorator('clientId', {
rules: [
{
required: true,
message: '请输入客户端id',
},
],
})(<Input placeholder="请输入客户端id" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="客户端密钥">
{getFieldDecorator('clientSecret', {
rules: [
{
required: true,
message: '请输入客户端密钥',
},
],
})(<Input placeholder="请输入客户端密钥" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="授权类型">
{getFieldDecorator('authorizedGrantTypes', {
rules: [
{
required: true,
message: '请输入授权类型',
},
],
initialValue: 'refresh_token,password,authorization_code',
})(<Input placeholder="请输入授权类型" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="授权范围">
{getFieldDecorator('scope', {
rules: [
{
required: true,
message: '请输入授权范围',
},
],
initialValue: 'all',
})(<Input placeholder="请输入授权范围" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="令牌过期秒数">
{getFieldDecorator('accessTokenValidity', {
rules: [
{
required: true,
message: '请输入令牌过期秒数',
},
],
initialValue: 3600,
})(<InputNumber placeholder="请输入令牌过期秒数" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="刷新令牌过期秒数">
{getFieldDecorator('refreshTokenValidity', {
rules: [
{
required: true,
message: '请输入刷新令牌过期秒数',
},
],
initialValue: 604800,
})(<InputNumber placeholder="请输入刷新令牌过期秒数" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="回调地址">
{getFieldDecorator('webServerRedirectUri', {
rules: [
{
required: true,
message: '请输入回调地址',
},
],
})(<Input placeholder="请输入回调地址" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="资源集合">
{getFieldDecorator('resourceIds', {})(<Input placeholder="请输入资源集合" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="权限">
{getFieldDecorator('authorities', {})(<Input placeholder="请输入权限" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="自动授权">
{getFieldDecorator('autoapprove', {})(<Input placeholder="请输入自动授权" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="附加说明">
{getFieldDecorator('additionalInformation', {})(
<Input placeholder="请输入附加说明" />
)}
</FormItem>
</Col>
</Row>
</Card>
</Form>
</Panel>
);
}
}
export default ClientAdd;

View File

@ -0,0 +1,219 @@
import React, { PureComponent } from 'react';
import { Form, Input, Card, Button, Row, Col, InputNumber } from 'antd';
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
import { CLIENT_DETAIL, CLIENT_SUBMIT } from '../../../actions/client';
const FormItem = Form.Item;
@connect(({ client, loading }) => ({
client,
submitting: loading.effects['client/submit'],
}))
@Form.create()
class ClientEdit extends PureComponent {
componentWillMount() {
const {
dispatch,
match: {
params: { id },
},
} = this.props;
dispatch(CLIENT_DETAIL(id));
}
handleSubmit = e => {
e.preventDefault();
const {
dispatch,
match: {
params: { id },
},
form,
} = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const params = {
id,
...values,
};
dispatch(CLIENT_SUBMIT(params));
}
});
};
render() {
const {
form: { getFieldDecorator },
client: { detail },
submitting,
} = this.props;
const formItemLayout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
const formAllItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
},
};
const action = (
<Button type="primary" onClick={this.handleSubmit} loading={submitting}>
提交
</Button>
);
return (
<Panel title="修改" back="/system/client" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="客户端id">
{getFieldDecorator('clientId', {
rules: [
{
required: true,
message: '请输入客户端id',
},
],
initialValue: detail.clientId,
})(<Input placeholder="请输入客户端id" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="客户端密钥">
{getFieldDecorator('clientSecret', {
rules: [
{
required: true,
message: '请输入客户端密钥',
},
],
initialValue: detail.clientSecret,
})(<Input placeholder="请输入客户端密钥" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="授权类型">
{getFieldDecorator('authorizedGrantTypes', {
rules: [
{
required: true,
message: '请输入授权类型',
},
],
initialValue: detail.authorizedGrantTypes,
})(<Input placeholder="请输入授权类型" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="授权范围">
{getFieldDecorator('scope', {
rules: [
{
required: true,
message: '请输入授权范围',
},
],
initialValue: detail.scope,
})(<Input placeholder="请输入授权范围" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="令牌过期秒数">
{getFieldDecorator('accessTokenValidity', {
rules: [
{
required: true,
message: '请输入令牌过期秒数',
},
],
initialValue: detail.accessTokenValidity,
})(<InputNumber placeholder="请输入令牌过期秒数" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="刷新令牌过期秒数">
{getFieldDecorator('refreshTokenValidity', {
rules: [
{
required: true,
message: '请输入刷新令牌过期秒数',
},
],
initialValue: detail.refreshTokenValidity,
})(<InputNumber placeholder="请输入刷新令牌过期秒数" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="回调地址">
{getFieldDecorator('webServerRedirectUri', {
rules: [
{
required: true,
message: '请输入回调地址',
},
],
initialValue: detail.webServerRedirectUri,
})(<Input placeholder="请输入回调地址" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="资源集合">
{getFieldDecorator('resourceIds', {
initialValue: detail.resourceIds,
})(<Input placeholder="请输入资源集合" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="权限">
{getFieldDecorator('authorities', {
initialValue: detail.authorities,
})(<Input placeholder="请输入权限" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="自动授权">
{getFieldDecorator('autoapprove', {
initialValue: detail.autoapprove,
})(<Input placeholder="请输入自动授权" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="附加说明">
{getFieldDecorator('additionalInformation', {
initialValue: detail.additionalInformation,
})(<Input placeholder="请输入附加说明" />)}
</FormItem>
</Col>
</Row>
</Card>
</Form>
</Panel>
);
}
}
export default ClientEdit;

View File

@ -0,0 +1,101 @@
import React, { PureComponent } from 'react';
import router from 'umi/router';
import { Form, Card, Button } from 'antd';
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
import { CLIENT_DETAIL } from '../../../actions/client';
const FormItem = Form.Item;
@connect(({ client }) => ({
client,
}))
@Form.create()
class ClientView extends PureComponent {
componentWillMount() {
const {
dispatch,
match: {
params: { id },
},
} = this.props;
dispatch(CLIENT_DETAIL(id));
}
handleEdit = () => {
const {
match: {
params: { id },
},
} = this.props;
router.push(`/system/client/edit/${id}`);
};
render() {
const {
client: { detail },
} = this.props;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
md: { span: 10 },
},
};
const action = (
<Button type="primary" onClick={this.handleEdit}>
修改
</Button>
);
return (
<Panel title="查看" back="/system/client" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}>
<FormItem {...formItemLayout} label="客户端id">
<span>{detail.clientId}</span>
</FormItem>
<FormItem {...formItemLayout} label="客户端密钥">
<span>{detail.clientSecret}</span>
</FormItem>
<FormItem {...formItemLayout} label="资源集合">
<span>{detail.resourceIds}</span>
</FormItem>
<FormItem {...formItemLayout} label="授权范围">
<span>{detail.scope}</span>
</FormItem>
<FormItem {...formItemLayout} label="授权类型">
<span>{detail.authorizedGrantTypes}</span>
</FormItem>
<FormItem {...formItemLayout} label="令牌过期秒数">
<span>{detail.accessTokenValidity}</span>
</FormItem>
<FormItem {...formItemLayout} label="刷新令牌过期秒数">
<span>{detail.refreshTokenValidity}</span>
</FormItem>
<FormItem {...formItemLayout} label="回调地址">
<span>{detail.webServerRedirectUri}</span>
</FormItem>
<FormItem {...formItemLayout} label="权限">
<span>{detail.authorities}</span>
</FormItem>
<FormItem {...formItemLayout} label="附加说明">
<span>{detail.additionalInformation}</span>
</FormItem>
<FormItem {...formItemLayout} label="自动授权">
<span>{detail.autoapprove}</span>
</FormItem>
</Card>
</Form>
</Panel>
);
}
}
export default ClientView;

View File

@ -4,6 +4,7 @@ import { Button, Col, Form, Input, Row } from 'antd';
import Panel from '../../../components/Panel';
import Grid from '../../../components/Sword/Grid';
import { DEPT_LIST } from '../../../actions/dept';
import { tenantMode } from '../../../defaultSettings';
const FormItem = Form.Item;
@ -65,14 +66,14 @@ class Dept extends PureComponent {
} = this.props;
const columns = [
{
title: '部门名称',
dataIndex: 'deptName',
},
{
title: '租户编号',
dataIndex: 'tenantCode',
},
{
title: '部门名称',
dataIndex: 'deptName',
},
{
title: '部门全称',
dataIndex: 'fullName',
@ -83,6 +84,10 @@ class Dept extends PureComponent {
},
];
if (!tenantMode) {
columns.splice(0, 1);
}
return (
<Panel>
<Grid

View File

@ -35,7 +35,6 @@ class DeptAdd extends PureComponent {
const { dispatch, form } = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log(values);
dispatch(DEPT_SUBMIT(values));
}
});

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { Form, Input, Card, Row, Col, Button, InputNumber, TreeSelect } from 'antd';
import { Form, Input, Card, Row, Col, Button, InputNumber, TreeSelect, message } from "antd";
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
@ -34,13 +34,17 @@ class DeptEdit extends PureComponent {
},
form,
} = this.props;
const parentId = form.getFieldValue('parentId');
if (id === parentId.toString()) {
message.warn('上级部门不能选择自身!');
return;
}
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const params = {
id,
...values,
};
console.log(params);
dispatch(DEPT_SUBMIT(params));
}
});

View File

@ -40,7 +40,6 @@ class DictAdd extends PureComponent {
const { dispatch, form } = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log(values);
dispatch(DICT_SUBMIT(values));
}
});

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { Form, Input, Card, Row, Col, Button, InputNumber, TreeSelect } from 'antd';
import { Form, Input, Card, Row, Col, Button, InputNumber, TreeSelect, message } from "antd";
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
@ -39,13 +39,17 @@ class DictEdit extends PureComponent {
},
form,
} = this.props;
const parentId = form.getFieldValue('parentId');
if (id === parentId.toString()) {
message.warn('上级字典不能选择自身!');
return;
}
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const params = {
id,
...values,
};
console.log(params);
dispatch(DICT_SUBMIT(params));
}
});

View File

@ -51,7 +51,6 @@ class MenuAdd extends PureComponent {
const { dispatch, form } = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log(values);
dispatch(MENU_SUBMIT(values));
}
});

View File

@ -11,6 +11,7 @@ import {
Radio,
Icon,
Modal,
message,
} from 'antd';
import { connect } from 'dva';
import Panel from '../../../components/Panel';
@ -51,13 +52,17 @@ class MenuEdit extends PureComponent {
},
form,
} = this.props;
const parentId = form.getFieldValue('parentId');
if (id === parentId.toString()) {
message.warn('上级菜单不能选择自身!');
return;
}
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const params = {
id,
...values,
};
console.log(params);
dispatch(MENU_SUBMIT(params));
}
});

View File

@ -11,6 +11,7 @@ import {
ROLE_GRANT,
} from '../../../actions/role';
import { MENU_REFRESH_DATA } from '../../../actions/menu';
import { tenantMode } from '../../../defaultSettings';
const FormItem = Form.Item;
const { TreeNode } = Tree;
@ -176,14 +177,14 @@ class Role extends PureComponent {
} = this.props;
const columns = [
{
title: '角色名称',
dataIndex: 'roleName',
},
{
title: '租户编号',
dataIndex: 'tenantCode',
},
{
title: '角色名称',
dataIndex: 'roleName',
},
{
title: '角色别名',
dataIndex: 'roleAlias',
@ -194,6 +195,10 @@ class Role extends PureComponent {
},
];
if (!tenantMode) {
columns.splice(0, 1);
}
return (
<Panel>
<Grid

View File

@ -35,7 +35,6 @@ class RoleAdd extends PureComponent {
const { dispatch, form } = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log(values);
dispatch(ROLE_SUBMIT(values));
}
});

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react';
import { Form, Input, Card, Row, Col, Button, InputNumber, TreeSelect } from 'antd';
import { Form, Input, Card, Row, Col, Button, InputNumber, TreeSelect, message } from "antd";
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
@ -34,13 +34,17 @@ class RoleEdit extends PureComponent {
},
form,
} = this.props;
const parentId = form.getFieldValue('parentId');
if (id === parentId.toString()) {
message.warn('上级角色不能选择自身!');
return;
}
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const params = {
id,
...values,
};
console.log(params);
dispatch(ROLE_SUBMIT(params));
}
});

View File

@ -5,6 +5,7 @@ import Panel from '../../../components/Panel';
import Grid from '../../../components/Sword/Grid';
import { USER_INIT, USER_LIST, USER_ROLE_GRANT } from '../../../actions/user';
import { resetPassword } from '../../../services/user';
import { tenantMode } from '../../../defaultSettings';
const FormItem = Form.Item;
const { TreeNode } = Tree;
@ -205,12 +206,12 @@ class User extends PureComponent {
title: '电子邮箱',
dataIndex: 'email',
},
{
title: '账号状态',
dataIndex: 'statusName',
},
];
if (!tenantMode) {
columns.splice(0, 1);
}
return (
<Panel>
<Grid

View File

@ -6,6 +6,7 @@ import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
import { USER_INIT, USER_CHANGE_INIT, USER_SUBMIT } from '../../../actions/user';
import func from '../../../utils/Func';
import { tenantMode } from '../../../defaultSettings';
const FormItem = Form.Item;
@ -66,6 +67,15 @@ class UserAdd extends PureComponent {
},
};
const formAllItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
},
};
const action = (
<Button type="primary" onClick={this.handleSubmit} loading={submitting}>
提交
@ -77,8 +87,8 @@ class UserAdd extends PureComponent {
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card title="基本信息" className={styles.card} bordered={false}>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="登录账号">
<Col span={20}>
<FormItem {...formAllItemLayout} label="登录账号">
{getFieldDecorator('account', {
rules: [
{
@ -89,35 +99,39 @@ class UserAdd extends PureComponent {
})(<Input placeholder="请输入登录账号" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="所属租户">
{getFieldDecorator('tenantCode', {
rules: [
{
required: true,
message: '请选择所属租户',
},
],
})(
<Select
showSearch
onChange={this.handleChange}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
allowClear
placeholder="请选择所属租户"
>
{tenantList.map(d => (
<Select.Option key={d.tenantCode} value={d.tenantCode}>
{d.tenantName}
</Select.Option>
))}
</Select>
)}
</FormItem>
</Col>
</Row>
{tenantMode ? (
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="所属租户">
{getFieldDecorator('tenantCode', {
rules: [
{
required: true,
message: '请选择所属租户',
},
],
})(
<Select
showSearch
onChange={this.handleChange}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
allowClear
placeholder="请选择所属租户"
>
{tenantList.map(d => (
<Select.Option key={d.tenantCode} value={d.tenantCode}>
{d.tenantName}
</Select.Option>
))}
</Select>
)}
</FormItem>
</Col>
</Row>
) : null}
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="密码">

View File

@ -6,6 +6,7 @@ import Panel from '../../../components/Panel';
import func from '../../../utils/Func';
import styles from '../../../layouts/Sword.less';
import { USER_CHANGE_INIT, USER_DETAIL, USER_INIT, USER_SUBMIT } from '../../../actions/user';
import { tenantMode } from '../../../defaultSettings';
const FormItem = Form.Item;
@ -65,8 +66,6 @@ class UserEdit extends PureComponent {
submitting,
} = this.props;
console.log(detail);
const formItemLayout = {
labelCol: {
span: 8,
@ -76,6 +75,15 @@ class UserEdit extends PureComponent {
},
};
const formAllItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
},
};
const action = (
<Button type="primary" onClick={this.handleSubmit} loading={submitting}>
提交
@ -87,8 +95,8 @@ class UserEdit extends PureComponent {
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card title="基本信息" className={styles.card} bordered={false}>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="登录账号">
<Col span={20}>
<FormItem {...formAllItemLayout} label="登录账号">
{getFieldDecorator('account', {
rules: [
{
@ -100,36 +108,40 @@ class UserEdit extends PureComponent {
})(<Input placeholder="请输入登录账号" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="所属租户">
{getFieldDecorator('tenantCode', {
rules: [
{
required: true,
message: '请选择所属租户',
},
],
initialValue: detail.tenantCode,
})(
<Select
showSearch
onChange={this.handleChange}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
allowClear
placeholder="请选择所属租户"
>
{tenantList.map(d => (
<Select.Option key={d.tenantCode} value={d.tenantCode}>
{d.tenantName}
</Select.Option>
))}
</Select>
)}
</FormItem>
</Col>
</Row>
{tenantMode ? (
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="所属租户">
{getFieldDecorator('tenantCode', {
rules: [
{
required: true,
message: '请选择所属租户',
},
],
initialValue: detail.tenantCode,
})(
<Select
showSearch
onChange={this.handleChange}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
allowClear
placeholder="请选择所属租户"
>
{tenantList.map(d => (
<Select.Option key={d.tenantCode} value={d.tenantCode}>
{d.tenantName}
</Select.Option>
))}
</Select>
)}
</FormItem>
</Col>
</Row>
) : null}
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="用户昵称">

View File

@ -5,6 +5,7 @@ import { connect } from 'dva';
import Panel from '../../../components/Panel';
import { USER_DETAIL } from '../../../actions/user';
import styles from '../../../layouts/Sword.less';
import { tenantMode } from '../../../defaultSettings';
const FormItem = Form.Item;
@ -46,6 +47,15 @@ class UserView extends PureComponent {
},
};
const formAllItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
},
};
const action = (
<Button type="primary" onClick={this.handleEdit}>
修改
@ -57,17 +67,21 @@ class UserView extends PureComponent {
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card title="基本信息" className={styles.card} bordered={false}>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="登录账号">
<Col span={20}>
<FormItem {...formAllItemLayout} label="登录账号">
<span>{detail.account}</span>
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="所属租户">
<span>{detail.tenantCode}</span>
</FormItem>
</Col>
</Row>
{tenantMode ? (
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="所属租户">
<span>{detail.tenantCode}</span>
</FormItem>
</Col>
</Row>
) : null}
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="用户昵称">

25
src/services/client.js Normal file
View File

@ -0,0 +1,25 @@
import { stringify } from 'qs';
import func from '../utils/Func';
import request from '../utils/request';
export async function list(params) {
return request(`/api/blade-system/client/list?${stringify(params)}`);
}
export async function submit(params) {
return request('/api/blade-system/client/submit', {
method: 'POST',
body: params,
});
}
export async function detail(params) {
return request(`/api/blade-system/client/detail?${stringify(params)}`);
}
export async function remove(params) {
return request('/api/blade-system/client/remove', {
method: 'POST',
body: func.toFormData(params),
});
}

View File

@ -2,6 +2,8 @@ import fetch from 'dva/fetch';
import { notification } from 'antd';
import router from 'umi/router';
import hash from 'hash.js';
import { Base64 } from 'js-base64';
import { clientId, clientSecret } from '../defaultSettings';
import { getToken, removeAll } from './authority';
const codeMessage = {
@ -113,12 +115,18 @@ export default function request(url, option) {
};
const newOptions = { ...defaultOptions, ...options };
// 将Token放至请求头
newOptions.headers = {
...newOptions.headers,
// 客户端认证
Authorization: `Basic ${Base64.encode(`${clientId}:${clientSecret}`)}`,
};
const token = getToken();
if (token) {
newOptions.headers = {
...newOptions.headers,
'blade-auth': token,
// token鉴权
'Blade-Auth': token,
};
}

View File

@ -7,5 +7,8 @@
"jsx-no-lambda": false,
"no-implicit-dependencies": false,
"no-console": false
},
"jsRules": {
"object-literal-sort-keys": false
}
}