mirror of
https://github.com/chillzhuang/Sword
synced 2024-12-22 08:59:24 +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">
|
||||
<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/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%20Boot-2.2.7.RELEASE-blue.svg" alt="Downloads">
|
||||
<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.9.RELEASE-blue.svg" alt="Downloads">
|
||||
<a target="_blank" href="https://bladex.vip">
|
||||
<img src="https://img.shields.io/badge/Author-Small%20Chill-ff69b4.svg" alt="Downloads">
|
||||
</a>
|
||||
@ -22,7 +22,7 @@
|
||||
* 极简封装了多租户底层,用更少的代码换来拓展性更强的SaaS多租户系统。
|
||||
* 借鉴OAuth2,实现了多终端认证系统,可控制子系统的token权限互相隔离。
|
||||
* 借鉴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`(满)
|
||||
* 交流二群:`751253339`(满)
|
||||
* 交流三群:`784729540`(满)
|
||||
* 交流四群:`1034621754`
|
||||
* 交流四群:`1034621754`(满)
|
||||
* 交流五群:`946350912`
|
||||
|
||||
## 在线演示
|
||||
* Saber-基于Vue:[https://saber.bladex.vip](https://saber.bladex.vip)
|
||||
|
@ -15,7 +15,7 @@ export default [
|
||||
path: '/',
|
||||
component: '../layouts/BasicLayout',
|
||||
Routes: ['src/pages/Authorized'],
|
||||
authority: ['administrator', 'admin', 'user', 'test'],
|
||||
authority: ['administrator', 'admin', 'user', 'test', 'guest'],
|
||||
routes: [
|
||||
// dashboard
|
||||
{ path: '/', redirect: '/dashboard/workplace' },
|
||||
|
@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "sword",
|
||||
"version": "2.7.0",
|
||||
"version": "2.7.2",
|
||||
"description": "An out-of-box UI solution for enterprise applications",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"presite": "cd functions && npm install",
|
||||
"start": "cross-env MOCK=none PORT=8888 umi dev",
|
||||
"start:no-mock": "cross-env MOCK=none PORT=8888 umi dev",
|
||||
"start:mock": "cross-env APP_TYPE=site PORT=8888 umi dev",
|
||||
"start": "cross-env MOCK=none PORT=1888 umi dev",
|
||||
"start:no-mock": "cross-env MOCK=none PORT=1888 umi dev",
|
||||
"start:mock": "cross-env APP_TYPE=site PORT=1888 umi dev",
|
||||
"build": "umi build",
|
||||
"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",
|
||||
|
@ -47,7 +47,7 @@ class WrapFormItem extends Component {
|
||||
refreshCaptcha = () => {
|
||||
// 获取验证码
|
||||
getCaptchaImage().then(resp => {
|
||||
const {data} = resp;
|
||||
const { data } = resp;
|
||||
if (data.key) {
|
||||
this.setState({ image: data.image });
|
||||
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,
|
||||
},
|
||||
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.tab-login-credentials': 'Credentials',
|
||||
'app.login.tab-login-mobile': 'Mobile number',
|
||||
'app.login.tab-login-social': 'Social System',
|
||||
'app.login.remember-me': 'Remember me',
|
||||
'app.login.forgot-password': 'Forgot your password?',
|
||||
'app.login.sign-in-with': 'Sign in with',
|
||||
|
@ -6,6 +6,7 @@ export default {
|
||||
'app.login.message-invalid-verification-code': '验证码错误',
|
||||
'app.login.tab-login-credentials': '账户密码登录',
|
||||
'app.login.tab-login-mobile': '手机号登录',
|
||||
'app.login.tab-login-social': '第三方系统登陆',
|
||||
'app.login.remember-me': '自动登录',
|
||||
'app.login.forgot-password': '忘记密码',
|
||||
'app.login.sign-in-with': '其他登录方式',
|
||||
|
@ -6,6 +6,7 @@ export default {
|
||||
'app.login.message-invalid-verification-code': '驗證碼錯誤',
|
||||
'app.login.tab-login-credentials': '賬戶密碼登錄',
|
||||
'app.login.tab-login-mobile': '手機號登錄',
|
||||
'app.login.tab-login-social': '第三方系統登陸',
|
||||
'app.login.remember-me': '自動登錄',
|
||||
'app.login.forgot-password': '忘記密碼',
|
||||
'app.login.sign-in-with': '其他登錄方式',
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { routerRedux } from 'dva/router';
|
||||
import { notification } from 'antd';
|
||||
import { stringify } from 'qs';
|
||||
import { getFakeCaptcha } from '../services/api';
|
||||
import { accountLogin } from '../services/user';
|
||||
import { accountLogin, socialLogin } from '../services/user';
|
||||
import { dynamicRoutes, dynamicButtons } from '../services/menu';
|
||||
import {
|
||||
setAuthority,
|
||||
@ -14,6 +15,7 @@ import {
|
||||
} from '../utils/authority';
|
||||
import { getPageQuery, formatRoutes, formatButtons } from '../utils/utils';
|
||||
import { reloadAuthorized } from '../utils/Authorized';
|
||||
import { getTopUrl } from '../utils/utils';
|
||||
|
||||
export default {
|
||||
namespace: 'login',
|
||||
@ -63,7 +65,29 @@ export default {
|
||||
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 }) {
|
||||
yield call(getFakeCaptcha, payload);
|
||||
},
|
||||
@ -95,16 +119,15 @@ export default {
|
||||
reducers: {
|
||||
changeLoginStatus(state, { payload }) {
|
||||
const { status, type } = payload;
|
||||
|
||||
if (status) {
|
||||
const {
|
||||
data: { tokenType, accessToken, authority, account, userName, avatar },
|
||||
data: { tokenType, accessToken, authority, account, userId, oauthId, userName, avatar },
|
||||
} = payload;
|
||||
const token = `${tokenType} ${accessToken}`;
|
||||
setToken(token);
|
||||
setAccessToken(accessToken);
|
||||
setAuthority(authority);
|
||||
setCurrentUser({ avatar, account, name: userName, authority });
|
||||
setCurrentUser({ avatar, userId, oauthId, account, name: userName, authority });
|
||||
} else {
|
||||
removeAll();
|
||||
}
|
||||
|
@ -236,11 +236,7 @@ class Region extends PureComponent {
|
||||
|
||||
const buttons = getButton('region');
|
||||
|
||||
const {
|
||||
treeData,
|
||||
treeCascader,
|
||||
debugVisible,
|
||||
} = this.state;
|
||||
const { treeData, treeCascader, debugVisible } = this.state;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
|
@ -2,7 +2,8 @@ import React, { PureComponent } from 'react';
|
||||
import { Card, Col, Collapse, Row, Divider, Tag } from 'antd';
|
||||
import styles from '../../layouts/Sword.less';
|
||||
|
||||
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
|
||||
import PageHeaderWrapper from '../../components/PageHeaderWrapper';
|
||||
import ThirdRegister from '../../components/ThirdRegister';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
|
||||
@ -11,6 +12,11 @@ class Workplace extends PureComponent {
|
||||
return (
|
||||
<PageHeaderWrapper>
|
||||
<Card className={styles.card} bordered={false}>
|
||||
<Row gutter={24}>
|
||||
<Col span={24}>
|
||||
<ThirdRegister />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
<Col span={24}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
@ -211,13 +217,15 @@ class Workplace extends PureComponent {
|
||||
<div>1.升级至 SpringCloud Hoxton.SR5</div>
|
||||
<div>2.升级至 SpringBoot 2.2.7.RELEASE</div>
|
||||
<div>3.升级至 Seata 1.2.0</div>
|
||||
<div>4.升级至 FastJson 1.2.70</div>
|
||||
<div>5.升级至 Avue 2.5.3</div>
|
||||
<div>6.新增行政区划管理模块</div>
|
||||
<div>7.优化用户导入的密码配置逻辑</div>
|
||||
<div>8.优化INode结构支持懒加载数据格式</div>
|
||||
<div>9.优化代码生成模板,支持最新版Saber结构</div>
|
||||
<div>10.修复Log模块在多线程、异步场景下报错的问题</div>
|
||||
<div>4.升级至 MybatisPlus 3.3.2</div>
|
||||
<div>5.升级至 Kinfe4j 2.0.3</div>
|
||||
<div>6.升级至 FastJson 1.2.70</div>
|
||||
<div>7.升级至 Avue 2.5.3</div>
|
||||
<div>8.新增行政区划管理模块</div>
|
||||
<div>9.优化用户导入的密码配置逻辑</div>
|
||||
<div>10.优化INode结构支持懒加载数据格式</div>
|
||||
<div>11.优化代码生成模板,支持最新版Saber结构</div>
|
||||
<div>12.修复Log模块在多线程、异步场景下报错的问题</div>
|
||||
</Panel>
|
||||
<Panel header="2.7.0发布 内核全面升级,增加岗位管理,用户导入导出" key="17">
|
||||
<div>1.升级至 SpringCloud Hoxton.SR3</div>
|
||||
|
@ -1,10 +1,11 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'dva';
|
||||
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 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;
|
||||
|
||||
@ -18,6 +19,36 @@ class LoginPage extends Component {
|
||||
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 => {
|
||||
this.setState({ type });
|
||||
};
|
||||
@ -53,6 +84,10 @@ class LoginPage extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = source => {
|
||||
window.location.href = `${authUrl}/${source}`;
|
||||
};
|
||||
|
||||
changeAutoLogin = e => {
|
||||
this.setState({
|
||||
autoLogin: e.target.checked,
|
||||
@ -65,7 +100,7 @@ class LoginPage extends Component {
|
||||
|
||||
render() {
|
||||
const { login, submitting } = this.props;
|
||||
const { type, autoLogin } = this.state;
|
||||
const { type, autoLogin, loading } = this.state;
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<Login
|
||||
@ -119,6 +154,66 @@ class LoginPage extends Component {
|
||||
/>
|
||||
{captchaMode ? <Captcha name="code" mode="image" /> : null}
|
||||
</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>
|
||||
<Checkbox checked={autoLogin} onChange={this.changeAutoLogin}>
|
||||
<FormattedMessage id="app.login.remember-me" />
|
||||
|
@ -29,4 +29,14 @@
|
||||
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 { connect } from 'dva';
|
||||
import {
|
||||
Upload,
|
||||
Icon,
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Input,
|
||||
message,
|
||||
Modal,
|
||||
Row,
|
||||
Tree,
|
||||
} from 'antd';
|
||||
import { Upload, Icon, Button, Col, Form, Input, message, Modal, Row, Tree } from 'antd';
|
||||
import Panel from '../../../components/Panel';
|
||||
import Grid from '../../../components/Sword/Grid';
|
||||
import { USER_INIT, USER_LIST, USER_ROLE_GRANT } from '../../../actions/user';
|
||||
@ -249,7 +238,6 @@ class User extends PureComponent {
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
render() {
|
||||
const code = 'user';
|
||||
|
||||
@ -269,7 +257,7 @@ class User extends PureComponent {
|
||||
headers: {
|
||||
'Blade-Auth': getToken(),
|
||||
},
|
||||
action: "/api/blade-user/import-user",
|
||||
action: '/api/blade-user/import-user',
|
||||
};
|
||||
|
||||
const formItemLayout = {
|
||||
|
@ -23,7 +23,7 @@ class UserEdit extends PureComponent {
|
||||
params: { id },
|
||||
},
|
||||
} = this.props;
|
||||
dispatch(USER_DETAIL(id)).then(()=>{
|
||||
dispatch(USER_DETAIL(id)).then(() => {
|
||||
const {
|
||||
user: { detail },
|
||||
} = this.props;
|
||||
@ -227,7 +227,7 @@ class UserEdit extends PureComponent {
|
||||
<Col span={10}>
|
||||
<FormItem {...formItemLayout} label="用户编号">
|
||||
{getFieldDecorator('code', {
|
||||
initialValue: detail.code
|
||||
initialValue: detail.code,
|
||||
})(<Input placeholder="请输入用户编号" />)}
|
||||
</FormItem>
|
||||
</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() {
|
||||
return request('/api/users');
|
||||
}
|
||||
|
@ -88,6 +88,9 @@ export default class Func {
|
||||
* @returns {string}
|
||||
*/
|
||||
static split(str) {
|
||||
if (String(str) === '-1') {
|
||||
return null;
|
||||
}
|
||||
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