🎉 2.6.1.RELEASE,增加登陆验证码,支持Seata1.0

This commit is contained in:
smallchill 2020-02-11 16:31:54 +08:00
parent 80f4b931a4
commit 9f259c1c12
11 changed files with 128 additions and 28 deletions

View File

@ -1,5 +1,5 @@
<p align="center">
<img src="https://img.shields.io/badge/Release-V2.6.0-green.svg" alt="Downloads">
<img src="https://img.shields.io/badge/Release-V2.6.1-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/license-Apache%202-blue.svg" alt="Build Status">
<img src="https://img.shields.io/badge/Spring%20Cloud-Hoxton.SR1-blue.svg" alt="Coverage Status">

View File

@ -1,6 +1,6 @@
{
"name": "sword",
"version": "2.6.0",
"version": "2.6.1",
"description": "An out-of-box UI solution for enterprise applications",
"private": true,
"scripts": {

View File

@ -1,9 +1,12 @@
import React, { Component } from 'react';
import { Form, Input, Button, Row, Col } from 'antd';
import { formatMessage } from 'umi/locale';
import omit from 'omit.js';
import styles from './index.less';
import ItemMap from './map';
import LoginContext from './loginContext';
import { getCaptchaImage } from '../../services/user';
import { setCaptchaKey } from '../../utils/authority';
const FormItem = Form.Item;
@ -17,9 +20,18 @@ class WrapFormItem extends Component {
super(props);
this.state = {
count: 0,
// 默认白色背景
image: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
};
}
componentWillMount() {
const { mode, type } = this.props;
if (type === 'Captcha' && mode === 'image') {
this.refreshCaptcha();
}
}
componentDidMount() {
const { updateActive, name } = this.props;
if (updateActive) {
@ -28,9 +40,21 @@ class WrapFormItem extends Component {
}
componentWillUnmount() {
// 清除计数器
clearInterval(this.interval);
}
refreshCaptcha = () => {
// 获取验证码
getCaptchaImage().then(resp => {
const {data} = resp;
if (data.key) {
this.setState({ image: data.image });
setCaptchaKey(data.key);
}
});
};
onGetCaptcha = () => {
const { onGetCaptcha } = this.props;
const result = onGetCaptcha ? onGetCaptcha() : null;
@ -71,7 +95,7 @@ class WrapFormItem extends Component {
};
render() {
const { count } = this.state;
const { count, image } = this.state;
const {
form: { getFieldDecorator },
@ -84,6 +108,7 @@ class WrapFormItem extends Component {
defaultValue,
rules,
name,
mode,
getCaptchaButtonText,
getCaptchaSecondText,
updateActive,
@ -96,26 +121,52 @@ class WrapFormItem extends Component {
const otherProps = restProps || {};
if (type === 'Captcha') {
const inputProps = omit(otherProps, ['onGetCaptcha', 'countDown']);
return (
<FormItem>
<Row gutter={8}>
<Col span={16}>
{getFieldDecorator(name, options)(<Input {...customprops} {...inputProps} />)}
</Col>
<Col span={8}>
<Button
disabled={count}
className={styles.getCaptcha}
size="large"
onClick={this.onGetCaptcha}
>
{count ? `${count} ${getCaptchaSecondText}` : getCaptchaButtonText}
</Button>
</Col>
</Row>
</FormItem>
);
if (mode === 'mobile') {
const inputProps = omit(otherProps, ['onGetCaptcha', 'countDown']);
return (
<FormItem>
<Row gutter={8}>
<Col span={16}>
{getFieldDecorator(name, options)(<Input {...customprops} {...inputProps} />)}
</Col>
<Col span={8}>
<Button
disabled={count}
className={styles.getCaptcha}
size="large"
onClick={this.onGetCaptcha}
>
{count ? `${count} ${getCaptchaSecondText}` : getCaptchaButtonText}
</Button>
</Col>
</Row>
</FormItem>
);
}
if (mode === 'image') {
return (
<FormItem>
<Row gutter={8}>
<Col span={16}>
{getFieldDecorator(name, options)(
<Input
{...customprops}
placeholder={`${formatMessage({ id: 'validation.captcha.required' })}`}
/>
)}
</Col>
<Col span={8}>
<img
alt="captcha"
src={image}
className={styles.getImgCaptcha}
onClick={this.refreshCaptcha}
/>
</Col>
</Row>
</FormItem>
);
}
}
return (
<FormItem>

View File

@ -18,6 +18,14 @@
width: 100%;
}
.getImgCaptcha {
display: block;
width: 100%;
cursor: pointer;
height: 40px;
border-radius: 5px;
}
.icon {
margin-left: 16px;
color: rgba(0, 0, 0, 0.2);

View File

@ -24,6 +24,7 @@ export default {
'validation.userName.tenantId': 'Please enter your tenantId!',
'validation.userName.required': 'Please enter your userName!',
'validation.password.required': 'Please enter your password!',
'validation.captcha.required': 'Please enter your captcha!',
'validation.password.twice': 'The passwords entered twice do not match!',
'validation.password.strength.msg':
"Please enter at least 6 characters and don't use passwords that are easy to guess.",

View File

@ -24,6 +24,7 @@ export default {
'validation.tenantId.required': '请输入租户ID!',
'validation.userName.required': '请输入用户名!',
'validation.password.required': '请输入密码!',
'validation.captcha.required': '请输入验证码!',
'validation.password.twice': '两次输入的密码不匹配!',
'validation.password.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
'validation.password.strength.strong': '强度:强',

View File

@ -24,6 +24,7 @@ export default {
'validation.tenantId.required': '請輸入租戶編號!',
'validation.userName.required': '請輸入賬戶!',
'validation.password.required': '請輸入密碼!',
'validation.captcha.required': '請輸入驗證碼!',
'validation.password.twice': '兩次輸入的密碼不匹配!',
'validation.password.strength.msg': '請至少輸入 6 個字符。請不要使用容易被猜到的密碼。',
'validation.password.strength.strong': '強度:強',

View File

@ -14,14 +14,14 @@ class Workplace extends PureComponent {
<Row gutter={24}>
<Col span={24}>
<div style={{ textAlign: 'center' }}>
<img src="https://img.shields.io/badge/Release-V2.6.0-green.svg" alt="Downloads" />
<img src="https://img.shields.io/badge/Release-V2.6.1-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/Spring%20Cloud-Hoxton.SR1-blue.svg"
alt="Coverage Status"
/>
<img
src="https://img.shields.io/badge/Spring%20Boot-2.2.2.RELEASE-blue.svg"
src="https://img.shields.io/badge/Spring%20Boot-2.2.4.RELEASE-blue.svg"
alt="Downloads"
/>
<a href="https://bladex.vip">
@ -206,7 +206,17 @@ class Workplace extends PureComponent {
</Row>
<Row gutter={24}>
<Card className={styles.card} bordered={false}>
<Collapse bordered={false} defaultActiveKey={['14']}>
<Collapse bordered={false} defaultActiveKey={['15']}>
<Panel header="2.6.1发布 增加登陆验证码支持seata1.0" key="15">
<div>1.升级SpringBoot 2.2.4.RELEASE</div>
<div>2.升级Alibaba Cloud 2.2.0.RELEASE</div>
<div>3.升级Mybatis-Plus 3.3.1</div>
<div>4.增加登陆验证码功能</div>
<div>5.增加验证码对应的CaptchaTokenGranter</div>
<div>6.增加RedisUtil方便业务操作</div>
<div>7.增加Condition类getQueryWrapper自定义排除参数的入口</div>
<div>8.优化Seata封装完美支持1.0.0版本</div>
</Panel>
<Panel header="2.6.0发布 升级Hoxton.SR1 适配最新架构" key="14">
<div>1.升级SpringCloud Hoxton.SR1</div>
<div>2.升级SpringBoot 2.2.2.RELEASE</div>

View File

@ -6,7 +6,7 @@ import Login from '../../components/Login';
import styles from './Login.less';
import { tenantMode } from '../../defaultSettings';
const { Tab, TenantId, UserName, Password, Submit } = Login;
const { Tab, TenantId, UserName, Password, Captcha, Submit } = Login;
@connect(({ login, loading }) => ({
login,
@ -117,6 +117,7 @@ class LoginPage extends Component {
]}
onPressEnter={() => this.loginForm.validateFields(this.handleSubmit)}
/>
<Captcha name="code" mode="image" />
</Tab>
<div>
<Checkbox checked={autoLogin} onChange={this.changeAutoLogin}>

View File

@ -1,11 +1,19 @@
import { stringify } from 'qs';
import request from '../utils/request';
import func from '../utils/Func';
import { getCaptchaKey } from '../utils/authority';
// =====================用户===========================
export async function accountLogin(params) {
const values = params;
values.grantType = 'captcha';
values.scope = 'all';
return request('/api/blade-auth/token', {
headers: {
'Captcha-key': getCaptchaKey(),
'Captcha-code': values.code,
},
method: 'POST',
body: func.toFormData(params),
});
@ -72,3 +80,7 @@ export async function updatePassword(params) {
body: func.toFormData(params),
});
}
export async function getCaptchaImage() {
return request('/api/blade-auth/captcha');
}

View File

@ -17,7 +17,12 @@ export function getAuthority(str) {
}
export function setAuthority(authority) {
const proAuthority = typeof authority === 'string' ? authority.split(',') : (typeof authority === 'undefined' ? null : authority);
const proAuthority =
typeof authority === 'string'
? authority.split(',')
: typeof authority === 'undefined'
? null
: authority;
return localStorage.setItem('sword-authority', JSON.stringify(proAuthority));
}
@ -63,10 +68,20 @@ export function setCurrentUser(account) {
localStorage.setItem('sword-current-user', JSON.stringify(account));
}
export function setCaptchaKey(key) {
localStorage.removeItem('sword-captcha-key');
localStorage.setItem('sword-captcha-key', key);
}
export function getCaptchaKey() {
return localStorage.getItem('sword-captcha-key');
}
export function removeAll() {
localStorage.removeItem('sword-authority');
localStorage.removeItem('sword-token');
localStorage.removeItem('sword-routes');
localStorage.removeItem('sword-buttons');
localStorage.removeItem('sword-current-user');
localStorage.removeItem('sword-captcha-key');
}