mirror of
https://github.com/chillzhuang/Sword
synced 2024-11-22 02:09:26 +08:00
🎉 2.7.2.RELEASE 集成JustAuth支持第三方登录
This commit is contained in:
parent
43a49063e2
commit
d1a0f5d8e8
11
README.md
11
README.md
@ -1,9 +1,9 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://img.shields.io/badge/Release-V2.7.1-green.svg" alt="Downloads">
|
<img src="https://img.shields.io/badge/Release-V2.7.2-green.svg" alt="Downloads">
|
||||||
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" alt="Build Status">
|
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" alt="Build Status">
|
||||||
<img src="https://img.shields.io/badge/license-Apache%202-blue.svg" alt="Build Status">
|
<img src="https://img.shields.io/badge/license-Apache%202-blue.svg" alt="Build Status">
|
||||||
<img src="https://img.shields.io/badge/Spring%20Cloud-Hoxton.SR5-blue.svg" alt="Coverage Status">
|
<img src="https://img.shields.io/badge/Spring%20Cloud-Hoxton.SR7-blue.svg" alt="Coverage Status">
|
||||||
<img src="https://img.shields.io/badge/Spring%20Boot-2.2.7.RELEASE-blue.svg" alt="Downloads">
|
<img src="https://img.shields.io/badge/Spring%20Boot-2.2.9.RELEASE-blue.svg" alt="Downloads">
|
||||||
<a target="_blank" href="https://bladex.vip">
|
<a target="_blank" href="https://bladex.vip">
|
||||||
<img src="https://img.shields.io/badge/Author-Small%20Chill-ff69b4.svg" alt="Downloads">
|
<img src="https://img.shields.io/badge/Author-Small%20Chill-ff69b4.svg" alt="Downloads">
|
||||||
</a>
|
</a>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
* 极简封装了多租户底层,用更少的代码换来拓展性更强的SaaS多租户系统。
|
* 极简封装了多租户底层,用更少的代码换来拓展性更强的SaaS多租户系统。
|
||||||
* 借鉴OAuth2,实现了多终端认证系统,可控制子系统的token权限互相隔离。
|
* 借鉴OAuth2,实现了多终端认证系统,可控制子系统的token权限互相隔离。
|
||||||
* 借鉴Security,封装了Secure模块,采用JWT做Token认证,可拓展集成Redis等细颗粒度控制方案。
|
* 借鉴Security,封装了Secure模块,采用JWT做Token认证,可拓展集成Redis等细颗粒度控制方案。
|
||||||
* 稳定生产了一年,经历了从Camden -> Greenwich的技术架构,也经历了从fat jar -> docker -> k8s + jenkins的部署架构
|
* 稳定生产了一年,经历了从Camden -> Hoxton的技术架构,也经历了从fat jar -> docker -> k8s + jenkins的部署架构
|
||||||
* 项目分包明确,规范微服务的开发模式,使包与包之间的分工清晰。
|
* 项目分包明确,规范微服务的开发模式,使包与包之间的分工清晰。
|
||||||
|
|
||||||
## 架构图
|
## 架构图
|
||||||
@ -59,7 +59,8 @@ SpringBlade
|
|||||||
* 交流一群:`477853168`(满)
|
* 交流一群:`477853168`(满)
|
||||||
* 交流二群:`751253339`(满)
|
* 交流二群:`751253339`(满)
|
||||||
* 交流三群:`784729540`(满)
|
* 交流三群:`784729540`(满)
|
||||||
* 交流四群:`1034621754`
|
* 交流四群:`1034621754`(满)
|
||||||
|
* 交流五群:`946350912`
|
||||||
|
|
||||||
## 在线演示
|
## 在线演示
|
||||||
* Saber-基于Vue:[https://saber.bladex.vip](https://saber.bladex.vip)
|
* Saber-基于Vue:[https://saber.bladex.vip](https://saber.bladex.vip)
|
||||||
|
@ -15,7 +15,7 @@ export default [
|
|||||||
path: '/',
|
path: '/',
|
||||||
component: '../layouts/BasicLayout',
|
component: '../layouts/BasicLayout',
|
||||||
Routes: ['src/pages/Authorized'],
|
Routes: ['src/pages/Authorized'],
|
||||||
authority: ['administrator', 'admin', 'user', 'test'],
|
authority: ['administrator', 'admin', 'user', 'test', 'guest'],
|
||||||
routes: [
|
routes: [
|
||||||
// dashboard
|
// dashboard
|
||||||
{ path: '/', redirect: '/dashboard/workplace' },
|
{ path: '/', redirect: '/dashboard/workplace' },
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "sword",
|
"name": "sword",
|
||||||
"version": "2.7.0",
|
"version": "2.7.2",
|
||||||
"description": "An out-of-box UI solution for enterprise applications",
|
"description": "An out-of-box UI solution for enterprise applications",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"presite": "cd functions && npm install",
|
"presite": "cd functions && npm install",
|
||||||
"start": "cross-env MOCK=none PORT=8888 umi dev",
|
"start": "cross-env MOCK=none PORT=1888 umi dev",
|
||||||
"start:no-mock": "cross-env MOCK=none PORT=8888 umi dev",
|
"start:no-mock": "cross-env MOCK=none PORT=1888 umi dev",
|
||||||
"start:mock": "cross-env APP_TYPE=site PORT=8888 umi dev",
|
"start:mock": "cross-env APP_TYPE=site PORT=1888 umi dev",
|
||||||
"build": "umi build",
|
"build": "umi build",
|
||||||
"site": "npm run presite && cross-env APP_TYPE=site npm run build && firebase deploy && npm run docker:push",
|
"site": "npm run presite && cross-env APP_TYPE=site npm run build && firebase deploy && npm run docker:push",
|
||||||
"analyze": "cross-env ANALYZE=1 umi build",
|
"analyze": "cross-env ANALYZE=1 umi build",
|
||||||
|
@ -47,7 +47,7 @@ class WrapFormItem extends Component {
|
|||||||
refreshCaptcha = () => {
|
refreshCaptcha = () => {
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
getCaptchaImage().then(resp => {
|
getCaptchaImage().then(resp => {
|
||||||
const {data} = resp;
|
const { data } = resp;
|
||||||
if (data.key) {
|
if (data.key) {
|
||||||
this.setState({ image: data.image });
|
this.setState({ image: data.image });
|
||||||
setCaptchaKey(data.key);
|
setCaptchaKey(data.key);
|
||||||
|
181
src/components/ThirdRegister/index.js
Normal file
181
src/components/ThirdRegister/index.js
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { connect } from 'dva';
|
||||||
|
import { Card, Col, Form, Input, Modal, Button, Row, message } from 'antd';
|
||||||
|
import styles from '@/layouts/Sword.less';
|
||||||
|
import { getCurrentUser, removeAll } from '@/utils/authority';
|
||||||
|
import { validateNull } from '@/utils/utils';
|
||||||
|
import { tenantMode } from '@/defaultSettings';
|
||||||
|
import { getUserInfo, registerGuest } from '@/services/user';
|
||||||
|
import router from 'umi/router';
|
||||||
|
|
||||||
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
@connect(({ tenant }) => ({
|
||||||
|
tenant,
|
||||||
|
}))
|
||||||
|
@Form.create()
|
||||||
|
class ThirdRegister extends PureComponent {
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
visible: false,
|
||||||
|
user: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const user = getCurrentUser();
|
||||||
|
if (validateNull(user) || validateNull(user.userId) || user.userId < 0) {
|
||||||
|
// 第三方注册用户,弹出注册框
|
||||||
|
this.setState({ visible: true, user });
|
||||||
|
} else {
|
||||||
|
// 获取用户信息,也可用于校验当前用户token是否有效
|
||||||
|
getUserInfo().then(resp => {
|
||||||
|
window.console.log(resp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const { form } = this.props;
|
||||||
|
const user = getCurrentUser();
|
||||||
|
form.validateFieldsAndScroll((err, values) => {
|
||||||
|
if (!err) {
|
||||||
|
const password = form.getFieldValue('password');
|
||||||
|
const password2 = form.getFieldValue('password2');
|
||||||
|
if (password !== password2) {
|
||||||
|
message.warning('两次密码输入不一致');
|
||||||
|
} else {
|
||||||
|
registerGuest(values, user.oauthId).then(resp => {
|
||||||
|
if (resp.success) {
|
||||||
|
this.setState({ visible: false });
|
||||||
|
Modal.success({ content: '注册申请已提交,请耐心等待管理员通过!' });
|
||||||
|
removeAll();
|
||||||
|
router.push('/user/login');
|
||||||
|
}
|
||||||
|
form.resetFields();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
form,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { loading, visible, user } = this.state;
|
||||||
|
|
||||||
|
const { getFieldDecorator } = form;
|
||||||
|
|
||||||
|
const tenantVisible = tenantMode;
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
span: 8,
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
span: 16,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const formAllItemLayout = {
|
||||||
|
labelCol: {
|
||||||
|
span: 4,
|
||||||
|
},
|
||||||
|
wrapperCol: {
|
||||||
|
span: 20,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="第三方系统用户注册"
|
||||||
|
width={800}
|
||||||
|
visible={visible}
|
||||||
|
closable={false}
|
||||||
|
footer={[
|
||||||
|
<Button key="submit" type="primary" loading={loading} onClick={this.handleSubmit}>
|
||||||
|
注册
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Form style={{ marginTop: 8 }}>
|
||||||
|
<Card className={styles.card} bordered={false}>
|
||||||
|
{tenantVisible ? (
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={20}>
|
||||||
|
<FormItem {...formAllItemLayout} label="租户编号">
|
||||||
|
{getFieldDecorator('tenantId', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入租户编号',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})(<Input placeholder="请输入租户编号" />)}
|
||||||
|
</FormItem>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
) : null}
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={10}>
|
||||||
|
<FormItem {...formItemLayout} label="用户姓名">
|
||||||
|
{getFieldDecorator('name', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入用户姓名',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
initialValue: user.name,
|
||||||
|
})(<Input placeholder="请输入用户姓名" />)}
|
||||||
|
</FormItem>
|
||||||
|
</Col>
|
||||||
|
<Col span={10}>
|
||||||
|
<FormItem {...formItemLayout} label="账号名称">
|
||||||
|
{getFieldDecorator('account', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入账号名称',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
initialValue: user.account,
|
||||||
|
})(<Input placeholder="请输入账号名称" />)}
|
||||||
|
</FormItem>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={10}>
|
||||||
|
<FormItem {...formItemLayout} label="账号密码">
|
||||||
|
{getFieldDecorator('password', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入密码',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})(<Input placeholder="请输入账号密码" />)}
|
||||||
|
</FormItem>
|
||||||
|
</Col>
|
||||||
|
<Col span={10}>
|
||||||
|
<FormItem {...formItemLayout} label="账号密码">
|
||||||
|
{getFieldDecorator('password2', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入确认密码',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})(<Input placeholder="请确认账号密码" />)}
|
||||||
|
</FormItem>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default ThirdRegister;
|
@ -16,4 +16,6 @@ module.exports = {
|
|||||||
disableLocal: false,
|
disableLocal: false,
|
||||||
},
|
},
|
||||||
pwa: true,
|
pwa: true,
|
||||||
|
// 第三方登陆授权地址
|
||||||
|
authUrl: 'http://localhost/blade-auth/oauth/render',
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@ export default {
|
|||||||
'app.login.message-invalid-verification-code': 'Invalid verification code',
|
'app.login.message-invalid-verification-code': 'Invalid verification code',
|
||||||
'app.login.tab-login-credentials': 'Credentials',
|
'app.login.tab-login-credentials': 'Credentials',
|
||||||
'app.login.tab-login-mobile': 'Mobile number',
|
'app.login.tab-login-mobile': 'Mobile number',
|
||||||
|
'app.login.tab-login-social': 'Social System',
|
||||||
'app.login.remember-me': 'Remember me',
|
'app.login.remember-me': 'Remember me',
|
||||||
'app.login.forgot-password': 'Forgot your password?',
|
'app.login.forgot-password': 'Forgot your password?',
|
||||||
'app.login.sign-in-with': 'Sign in with',
|
'app.login.sign-in-with': 'Sign in with',
|
||||||
|
@ -6,6 +6,7 @@ export default {
|
|||||||
'app.login.message-invalid-verification-code': '验证码错误',
|
'app.login.message-invalid-verification-code': '验证码错误',
|
||||||
'app.login.tab-login-credentials': '账户密码登录',
|
'app.login.tab-login-credentials': '账户密码登录',
|
||||||
'app.login.tab-login-mobile': '手机号登录',
|
'app.login.tab-login-mobile': '手机号登录',
|
||||||
|
'app.login.tab-login-social': '第三方系统登陆',
|
||||||
'app.login.remember-me': '自动登录',
|
'app.login.remember-me': '自动登录',
|
||||||
'app.login.forgot-password': '忘记密码',
|
'app.login.forgot-password': '忘记密码',
|
||||||
'app.login.sign-in-with': '其他登录方式',
|
'app.login.sign-in-with': '其他登录方式',
|
||||||
|
@ -6,6 +6,7 @@ export default {
|
|||||||
'app.login.message-invalid-verification-code': '驗證碼錯誤',
|
'app.login.message-invalid-verification-code': '驗證碼錯誤',
|
||||||
'app.login.tab-login-credentials': '賬戶密碼登錄',
|
'app.login.tab-login-credentials': '賬戶密碼登錄',
|
||||||
'app.login.tab-login-mobile': '手機號登錄',
|
'app.login.tab-login-mobile': '手機號登錄',
|
||||||
|
'app.login.tab-login-social': '第三方系統登陸',
|
||||||
'app.login.remember-me': '自動登錄',
|
'app.login.remember-me': '自動登錄',
|
||||||
'app.login.forgot-password': '忘記密碼',
|
'app.login.forgot-password': '忘記密碼',
|
||||||
'app.login.sign-in-with': '其他登錄方式',
|
'app.login.sign-in-with': '其他登錄方式',
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { routerRedux } from 'dva/router';
|
import { routerRedux } from 'dva/router';
|
||||||
|
import { notification } from 'antd';
|
||||||
import { stringify } from 'qs';
|
import { stringify } from 'qs';
|
||||||
import { getFakeCaptcha } from '../services/api';
|
import { getFakeCaptcha } from '../services/api';
|
||||||
import { accountLogin } from '../services/user';
|
import { accountLogin, socialLogin } from '../services/user';
|
||||||
import { dynamicRoutes, dynamicButtons } from '../services/menu';
|
import { dynamicRoutes, dynamicButtons } from '../services/menu';
|
||||||
import {
|
import {
|
||||||
setAuthority,
|
setAuthority,
|
||||||
@ -14,6 +15,7 @@ import {
|
|||||||
} from '../utils/authority';
|
} from '../utils/authority';
|
||||||
import { getPageQuery, formatRoutes, formatButtons } from '../utils/utils';
|
import { getPageQuery, formatRoutes, formatButtons } from '../utils/utils';
|
||||||
import { reloadAuthorized } from '../utils/Authorized';
|
import { reloadAuthorized } from '../utils/Authorized';
|
||||||
|
import { getTopUrl } from '../utils/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespace: 'login',
|
namespace: 'login',
|
||||||
@ -63,7 +65,29 @@ export default {
|
|||||||
yield put(routerRedux.replace(redirect || '/'));
|
yield put(routerRedux.replace(redirect || '/'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
*socialLogin({ payload }, { call, put }) {
|
||||||
|
const response = yield call(socialLogin, payload);
|
||||||
|
if (response.success) {
|
||||||
|
yield put({
|
||||||
|
type: 'changeLoginStatus',
|
||||||
|
payload: {
|
||||||
|
status: true,
|
||||||
|
type: 'login',
|
||||||
|
data: { ...response.data },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
reloadAuthorized();
|
||||||
|
const topUrl = getTopUrl();
|
||||||
|
const redirectUrl = '/oauth/redirect/';
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
window.location.href = topUrl.split(redirectUrl)[0];
|
||||||
|
yield put(routerRedux.replace('/'));
|
||||||
|
} else {
|
||||||
|
notification.error({
|
||||||
|
message: response.msg,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
*getCaptcha({ payload }, { call }) {
|
*getCaptcha({ payload }, { call }) {
|
||||||
yield call(getFakeCaptcha, payload);
|
yield call(getFakeCaptcha, payload);
|
||||||
},
|
},
|
||||||
@ -95,16 +119,15 @@ export default {
|
|||||||
reducers: {
|
reducers: {
|
||||||
changeLoginStatus(state, { payload }) {
|
changeLoginStatus(state, { payload }) {
|
||||||
const { status, type } = payload;
|
const { status, type } = payload;
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
const {
|
const {
|
||||||
data: { tokenType, accessToken, authority, account, userName, avatar },
|
data: { tokenType, accessToken, authority, account, userId, oauthId, userName, avatar },
|
||||||
} = payload;
|
} = payload;
|
||||||
const token = `${tokenType} ${accessToken}`;
|
const token = `${tokenType} ${accessToken}`;
|
||||||
setToken(token);
|
setToken(token);
|
||||||
setAccessToken(accessToken);
|
setAccessToken(accessToken);
|
||||||
setAuthority(authority);
|
setAuthority(authority);
|
||||||
setCurrentUser({ avatar, account, name: userName, authority });
|
setCurrentUser({ avatar, userId, oauthId, account, name: userName, authority });
|
||||||
} else {
|
} else {
|
||||||
removeAll();
|
removeAll();
|
||||||
}
|
}
|
||||||
|
@ -236,11 +236,7 @@ class Region extends PureComponent {
|
|||||||
|
|
||||||
const buttons = getButton('region');
|
const buttons = getButton('region');
|
||||||
|
|
||||||
const {
|
const { treeData, treeCascader, debugVisible } = this.state;
|
||||||
treeData,
|
|
||||||
treeCascader,
|
|
||||||
debugVisible,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const formItemLayout = {
|
const formItemLayout = {
|
||||||
labelCol: {
|
labelCol: {
|
||||||
|
@ -2,7 +2,8 @@ import React, { PureComponent } from 'react';
|
|||||||
import { Card, Col, Collapse, Row, Divider, Tag } from 'antd';
|
import { Card, Col, Collapse, Row, Divider, Tag } from 'antd';
|
||||||
import styles from '../../layouts/Sword.less';
|
import styles from '../../layouts/Sword.less';
|
||||||
|
|
||||||
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
|
import PageHeaderWrapper from '../../components/PageHeaderWrapper';
|
||||||
|
import ThirdRegister from '../../components/ThirdRegister';
|
||||||
|
|
||||||
const { Panel } = Collapse;
|
const { Panel } = Collapse;
|
||||||
|
|
||||||
@ -11,6 +12,11 @@ class Workplace extends PureComponent {
|
|||||||
return (
|
return (
|
||||||
<PageHeaderWrapper>
|
<PageHeaderWrapper>
|
||||||
<Card className={styles.card} bordered={false}>
|
<Card className={styles.card} bordered={false}>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={24}>
|
||||||
|
<ThirdRegister />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row gutter={24}>
|
<Row gutter={24}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<div style={{ textAlign: 'center' }}>
|
<div style={{ textAlign: 'center' }}>
|
||||||
@ -211,13 +217,15 @@ class Workplace extends PureComponent {
|
|||||||
<div>1.升级至 SpringCloud Hoxton.SR5</div>
|
<div>1.升级至 SpringCloud Hoxton.SR5</div>
|
||||||
<div>2.升级至 SpringBoot 2.2.7.RELEASE</div>
|
<div>2.升级至 SpringBoot 2.2.7.RELEASE</div>
|
||||||
<div>3.升级至 Seata 1.2.0</div>
|
<div>3.升级至 Seata 1.2.0</div>
|
||||||
<div>4.升级至 FastJson 1.2.70</div>
|
<div>4.升级至 MybatisPlus 3.3.2</div>
|
||||||
<div>5.升级至 Avue 2.5.3</div>
|
<div>5.升级至 Kinfe4j 2.0.3</div>
|
||||||
<div>6.新增行政区划管理模块</div>
|
<div>6.升级至 FastJson 1.2.70</div>
|
||||||
<div>7.优化用户导入的密码配置逻辑</div>
|
<div>7.升级至 Avue 2.5.3</div>
|
||||||
<div>8.优化INode结构支持懒加载数据格式</div>
|
<div>8.新增行政区划管理模块</div>
|
||||||
<div>9.优化代码生成模板,支持最新版Saber结构</div>
|
<div>9.优化用户导入的密码配置逻辑</div>
|
||||||
<div>10.修复Log模块在多线程、异步场景下报错的问题</div>
|
<div>10.优化INode结构支持懒加载数据格式</div>
|
||||||
|
<div>11.优化代码生成模板,支持最新版Saber结构</div>
|
||||||
|
<div>12.修复Log模块在多线程、异步场景下报错的问题</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel header="2.7.0发布 内核全面升级,增加岗位管理,用户导入导出" key="17">
|
<Panel header="2.7.0发布 内核全面升级,增加岗位管理,用户导入导出" key="17">
|
||||||
<div>1.升级至 SpringCloud Hoxton.SR3</div>
|
<div>1.升级至 SpringCloud Hoxton.SR3</div>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import { formatMessage, FormattedMessage } from 'umi/locale';
|
import { formatMessage, FormattedMessage } from 'umi/locale';
|
||||||
import { Checkbox, Alert } from 'antd';
|
import { Checkbox, Alert, Icon, Row, Col, Card, Spin } from 'antd';
|
||||||
import Login from '../../components/Login';
|
import Login from '../../components/Login';
|
||||||
import styles from './Login.less';
|
import styles from './Login.less';
|
||||||
import { tenantMode, captchaMode } from '../../defaultSettings';
|
import { tenantMode, captchaMode, authUrl } from '../../defaultSettings';
|
||||||
|
import { getQueryString, getTopUrl, validateNull } from '@/utils/utils';
|
||||||
|
|
||||||
const { Tab, TenantId, UserName, Password, Captcha, Submit } = Login;
|
const { Tab, TenantId, UserName, Password, Captcha, Submit } = Login;
|
||||||
|
|
||||||
@ -18,6 +19,36 @@ class LoginPage extends Component {
|
|||||||
autoLogin: true,
|
autoLogin: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const domain = getTopUrl();
|
||||||
|
const redirectUrl = '/oauth/redirect/';
|
||||||
|
const {
|
||||||
|
dispatch,
|
||||||
|
route: { routes, authority },
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let source = getQueryString('source');
|
||||||
|
const code = getQueryString('code');
|
||||||
|
const state = getQueryString('state');
|
||||||
|
if (validateNull(source) && domain.includes(redirectUrl)) {
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
source = domain.split('?')[0];
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
source = source.split(redirectUrl)[1];
|
||||||
|
}
|
||||||
|
if (!validateNull(source) && !validateNull(code) && !validateNull(state)) {
|
||||||
|
dispatch({
|
||||||
|
type: 'login/socialLogin',
|
||||||
|
payload: { source, code, state, tenantId: '000000' },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: 'menu/fetchMenuData',
|
||||||
|
payload: { routes, authority },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onTabChange = type => {
|
onTabChange = type => {
|
||||||
this.setState({ type });
|
this.setState({ type });
|
||||||
};
|
};
|
||||||
@ -53,6 +84,10 @@ class LoginPage extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleClick = source => {
|
||||||
|
window.location.href = `${authUrl}/${source}`;
|
||||||
|
};
|
||||||
|
|
||||||
changeAutoLogin = e => {
|
changeAutoLogin = e => {
|
||||||
this.setState({
|
this.setState({
|
||||||
autoLogin: e.target.checked,
|
autoLogin: e.target.checked,
|
||||||
@ -65,7 +100,7 @@ class LoginPage extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { login, submitting } = this.props;
|
const { login, submitting } = this.props;
|
||||||
const { type, autoLogin } = this.state;
|
const { type, autoLogin, loading } = this.state;
|
||||||
return (
|
return (
|
||||||
<div className={styles.main}>
|
<div className={styles.main}>
|
||||||
<Login
|
<Login
|
||||||
@ -119,6 +154,66 @@ class LoginPage extends Component {
|
|||||||
/>
|
/>
|
||||||
{captchaMode ? <Captcha name="code" mode="image" /> : null}
|
{captchaMode ? <Captcha name="code" mode="image" /> : null}
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab key="social" tab={formatMessage({ id: 'app.login.tab-login-social' })}>
|
||||||
|
<Card className={styles.card} bordered={false}>
|
||||||
|
<Row gutter={24} className={styles.iconPreview}>
|
||||||
|
<Col span={4} key="github">
|
||||||
|
<Icon
|
||||||
|
type="github"
|
||||||
|
theme="filled"
|
||||||
|
onClick={() => {
|
||||||
|
this.handleClick('github');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={4} key="gitee">
|
||||||
|
<Icon
|
||||||
|
type="google-circle"
|
||||||
|
theme="filled"
|
||||||
|
onClick={() => {
|
||||||
|
this.handleClick('gitee');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={4} key="wechat">
|
||||||
|
<Icon
|
||||||
|
type="wechat"
|
||||||
|
theme="filled"
|
||||||
|
onClick={() => {
|
||||||
|
this.handleClick('wechat_open');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={4} key="dingtalk">
|
||||||
|
<Icon
|
||||||
|
type="dingtalk-circle"
|
||||||
|
theme="filled"
|
||||||
|
onClick={() => {
|
||||||
|
this.handleClick('dingtalk');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={4} key="alipay">
|
||||||
|
<Icon
|
||||||
|
type="alipay-circle"
|
||||||
|
theme="filled"
|
||||||
|
onClick={() => {
|
||||||
|
this.handleClick('alipay');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={4} key="taobao">
|
||||||
|
<Icon
|
||||||
|
type="taobao-circle"
|
||||||
|
theme="filled"
|
||||||
|
onClick={() => {
|
||||||
|
this.handleClick('taobao');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
</Tab>
|
||||||
<div>
|
<div>
|
||||||
<Checkbox checked={autoLogin} onChange={this.changeAutoLogin}>
|
<Checkbox checked={autoLogin} onChange={this.changeAutoLogin}>
|
||||||
<FormattedMessage id="app.login.remember-me" />
|
<FormattedMessage id="app.login.remember-me" />
|
||||||
|
@ -29,4 +29,14 @@
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconPreview {
|
||||||
|
font-size: 45px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import {
|
import { Upload, Icon, Button, Col, Form, Input, message, Modal, Row, Tree } from 'antd';
|
||||||
Upload,
|
|
||||||
Icon,
|
|
||||||
Button,
|
|
||||||
Col,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
message,
|
|
||||||
Modal,
|
|
||||||
Row,
|
|
||||||
Tree,
|
|
||||||
} from 'antd';
|
|
||||||
import Panel from '../../../components/Panel';
|
import Panel from '../../../components/Panel';
|
||||||
import Grid from '../../../components/Sword/Grid';
|
import Grid from '../../../components/Sword/Grid';
|
||||||
import { USER_INIT, USER_LIST, USER_ROLE_GRANT } from '../../../actions/user';
|
import { USER_INIT, USER_LIST, USER_ROLE_GRANT } from '../../../actions/user';
|
||||||
@ -249,7 +238,6 @@ class User extends PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const code = 'user';
|
const code = 'user';
|
||||||
|
|
||||||
@ -269,7 +257,7 @@ class User extends PureComponent {
|
|||||||
headers: {
|
headers: {
|
||||||
'Blade-Auth': getToken(),
|
'Blade-Auth': getToken(),
|
||||||
},
|
},
|
||||||
action: "/api/blade-user/import-user",
|
action: '/api/blade-user/import-user',
|
||||||
};
|
};
|
||||||
|
|
||||||
const formItemLayout = {
|
const formItemLayout = {
|
||||||
|
@ -23,7 +23,7 @@ class UserEdit extends PureComponent {
|
|||||||
params: { id },
|
params: { id },
|
||||||
},
|
},
|
||||||
} = this.props;
|
} = this.props;
|
||||||
dispatch(USER_DETAIL(id)).then(()=>{
|
dispatch(USER_DETAIL(id)).then(() => {
|
||||||
const {
|
const {
|
||||||
user: { detail },
|
user: { detail },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -227,7 +227,7 @@ class UserEdit extends PureComponent {
|
|||||||
<Col span={10}>
|
<Col span={10}>
|
||||||
<FormItem {...formItemLayout} label="用户编号">
|
<FormItem {...formItemLayout} label="用户编号">
|
||||||
{getFieldDecorator('code', {
|
{getFieldDecorator('code', {
|
||||||
initialValue: detail.code
|
initialValue: detail.code,
|
||||||
})(<Input placeholder="请输入用户编号" />)}
|
})(<Input placeholder="请输入用户编号" />)}
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -20,6 +20,25 @@ export async function accountLogin(params) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function socialLogin(params) {
|
||||||
|
const values = params;
|
||||||
|
values.grantType = 'social';
|
||||||
|
values.scope = 'all';
|
||||||
|
return request('/api/blade-auth/token', {
|
||||||
|
method: 'POST',
|
||||||
|
body: func.toFormData(values),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function registerGuest(form, oauthId) {
|
||||||
|
const values = form;
|
||||||
|
values.oauthId = oauthId;
|
||||||
|
return request('/api/blade-user/register-guest', {
|
||||||
|
method: 'POST',
|
||||||
|
body: func.toFormData(values),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function query() {
|
export async function query() {
|
||||||
return request('/api/users');
|
return request('/api/users');
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,9 @@ export default class Func {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
static split(str) {
|
static split(str) {
|
||||||
|
if (String(str) === '-1') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return str ? String(str).split(',') : '';
|
return str ? String(str).split(',') : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
src/utils/getPageTitle.js
Normal file
27
src/utils/getPageTitle.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { formatMessage } from 'umi/locale';
|
||||||
|
import pathToRegexp from 'path-to-regexp';
|
||||||
|
import isEqual from 'lodash/isEqual';
|
||||||
|
import memoizeOne from 'memoize-one';
|
||||||
|
import { menu, title } from '../defaultSettings';
|
||||||
|
|
||||||
|
export const matchParamsPath = (pathname, breadcrumbNameMap) => {
|
||||||
|
const pathKey = Object.keys(breadcrumbNameMap).find(key => pathToRegexp(key).test(pathname));
|
||||||
|
return breadcrumbNameMap[pathKey];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPageTitle = (pathname, breadcrumbNameMap) => {
|
||||||
|
const currRouterData = matchParamsPath(pathname, breadcrumbNameMap);
|
||||||
|
if (!currRouterData) {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
const pageName = menu.disableLocal
|
||||||
|
? currRouterData.name
|
||||||
|
: formatMessage({
|
||||||
|
id: currRouterData.locale || currRouterData.name,
|
||||||
|
defaultMessage: currRouterData.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
return `${pageName} - ${title}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memoizeOne(getPageTitle, isEqual);
|
@ -212,3 +212,45 @@ export function formatButtons(buttons) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为空
|
||||||
|
*/
|
||||||
|
export function validateNull(val) {
|
||||||
|
if (typeof val === 'boolean') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeof val === 'number') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (val instanceof Array) {
|
||||||
|
if (val.length === 0) return true;
|
||||||
|
} else if (val instanceof Object) {
|
||||||
|
if (JSON.stringify(val) === '{}') return true;
|
||||||
|
} else {
|
||||||
|
if (val === 'null' || val == null || val === 'undefined' || val === undefined || val === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取顶部地址栏地址
|
||||||
|
*/
|
||||||
|
export function getTopUrl() {
|
||||||
|
return window.location.href.split('/#/')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取url参数
|
||||||
|
* @param name 参数名
|
||||||
|
*/
|
||||||
|
export function getQueryString(name) {
|
||||||
|
// eslint-disable-next-line no-shadow
|
||||||
|
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
|
||||||
|
const r = window.location.search.substr(1).match(reg);
|
||||||
|
if (r != null) return unescape(decodeURI(r[2]));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user