🎉 2.5.2.RELEASE 增加个人中心

This commit is contained in:
smallchill 2019-10-20 22:55:46 +08:00
parent 21e8a20f0a
commit 9acf4e2984
16 changed files with 319 additions and 151 deletions

View File

@ -52,18 +52,19 @@ export default [
}, },
{ {
path: '/account/settings', path: '/account/settings',
component: './Account/Settings/Info', //component: './Account/Settings/Info',
routes: [ routes: [
{ path: '/account/settings', redirect: '/account/settings/base' }, { path: '/account/settings', redirect: '/account/settings/base' },
{ path: '/account/settings/base', component: './Account/Settings/BaseView' }, { path: '/account/settings/base', component: './Account/Settings/BaseView' },
{ path: '/account/settings/security', component: './Account/Settings/SecurityView' }, { path: '/account/settings/password', component: './Account/Settings/PasswordView' },
{ path: '/account/settings/binding', component: './Account/Settings/BindingView' }, //{ path: '/account/settings/security', component: './Account/Settings/SecurityView' },
//{ path: '/account/settings/binding', component: './Account/Settings/BindingView' },
{ {
path: '/account/settings/notification', path: '/account/settings/notification',
component: './Account/Settings/NotificationView', component: './Account/Settings/NotificationView',
}, },
], ],
}, }
], ],
}, },
{ {

View File

@ -1,6 +1,6 @@
{ {
"name": "sword", "name": "sword",
"version": "2.5.1", "version": "2.5.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": {

View File

@ -96,14 +96,14 @@ export default class GlobalHeaderRight extends PureComponent {
selectedKeys={[]} selectedKeys={[]}
onClick={onMenuClick} onClick={onMenuClick}
> >
<Menu.Item key="userCenter">
<Icon type="user" />
<FormattedMessage id="menu.account.center" defaultMessage="account center" />
</Menu.Item>
<Menu.Item key="userinfo"> <Menu.Item key="userinfo">
<Icon type="setting" /> <Icon type="setting" />
<FormattedMessage id="menu.account.settings" defaultMessage="account settings" /> <FormattedMessage id="menu.account.settings" defaultMessage="account settings" />
</Menu.Item> </Menu.Item>
<Menu.Item key="password">
<Icon type="user" />
<FormattedMessage id="menu.account.password" defaultMessage="password settings" />
</Menu.Item>
<Menu.Divider /> <Menu.Divider />
<Menu.Item key="logout"> <Menu.Item key="logout">
<Icon type="logout" /> <Icon type="logout" />

View File

@ -52,10 +52,12 @@ export default class PageHeader extends PureComponent {
<div className={styles.detail}> <div className={styles.detail}>
{logo && <div className={styles.logo}>{logo}</div>} {logo && <div className={styles.logo}>{logo}</div>}
<div className={styles.main}> <div className={styles.main}>
<div className={styles.row}> {title && (
{title && <h1 className={styles.title}>{title}</h1>} <div className={styles.row}>
{action && <div className={styles.action}>{action}</div>} <h1 className={styles.title}>{title}</h1>
</div> {action && <div className={styles.action}>{action}</div>}
</div>
)}
<div className={styles.row}> <div className={styles.row}>
{content && <div className={styles.content}>{content}</div>} {content && <div className={styles.content}>{content}</div>}
{extraContent && <div className={styles.extraContent}>{extraContent}</div>} {extraContent && <div className={styles.extraContent}>{extraContent}</div>}

View File

@ -62,8 +62,11 @@ class HeaderView extends React.Component {
return; return;
} }
if (key === 'userinfo') { if (key === 'userinfo') {
message.success('即将开放'); router.push('/account/settings/base');
// router.push('/account/settings/base'); return;
}
if (key === 'password') {
router.push('/account/settings/password');
return; return;
} }
if (key === 'triggerError') { if (key === 'triggerError') {

View File

@ -35,6 +35,7 @@ export default {
'menu.exception.trigger': 'Trigger', 'menu.exception.trigger': 'Trigger',
'menu.account': 'Account', 'menu.account': 'Account',
'menu.account.center': 'Account Center', 'menu.account.center': 'Account Center',
'menu.account.password': 'Modify Password',
'menu.account.settings': 'Account Settings', 'menu.account.settings': 'Account Settings',
'menu.account.trigger': 'Trigger Error', 'menu.account.trigger': 'Trigger Error',
'menu.account.logout': 'Logout', 'menu.account.logout': 'Logout',

View File

@ -9,6 +9,8 @@ export default {
'app.settings.basic.email-message': 'Please input your email!', 'app.settings.basic.email-message': 'Please input your email!',
'app.settings.basic.nickname': 'Nickname', 'app.settings.basic.nickname': 'Nickname',
'app.settings.basic.nickname-message': 'Please input your Nickname!', 'app.settings.basic.nickname-message': 'Please input your Nickname!',
'app.settings.basic.realname': 'Realname',
'app.settings.basic.realname-message': 'Please input your Realname!',
'app.settings.basic.profile': 'Personal profile', 'app.settings.basic.profile': 'Personal profile',
'app.settings.basic.profile-message': 'Please input your personal profile!', 'app.settings.basic.profile-message': 'Please input your personal profile!',
'app.settings.basic.profile-placeholder': 'Brief introduction to yourself', 'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',
@ -21,6 +23,12 @@ export default {
'app.settings.basic.phone': 'Phone Number', 'app.settings.basic.phone': 'Phone Number',
'app.settings.basic.phone-message': 'Please input your phone!', 'app.settings.basic.phone-message': 'Please input your phone!',
'app.settings.basic.update': 'Update Information', 'app.settings.basic.update': 'Update Information',
'app.settings.password.old': 'Old Password',
'app.settings.password.old-message': 'Please input your Old Password!',
'app.settings.password.new': 'New Password',
'app.settings.password.new-message': 'Please input your new Password!',
'app.settings.password.new1': 'New Password1',
'app.settings.password.new1-message': 'Please comfirm your new Password!',
'app.settings.security.strong': 'Strong', 'app.settings.security.strong': 'Strong',
'app.settings.security.medium': 'Medium', 'app.settings.security.medium': 'Medium',
'app.settings.security.weak': 'Weak', 'app.settings.security.weak': 'Weak',

View File

@ -35,6 +35,7 @@ export default {
'menu.exception.trigger': '触发错误', 'menu.exception.trigger': '触发错误',
'menu.account': '个人页', 'menu.account': '个人页',
'menu.account.center': '个人中心', 'menu.account.center': '个人中心',
'menu.account.password': '密码修改',
'menu.account.settings': '个人设置', 'menu.account.settings': '个人设置',
'menu.account.trigger': '触发报错', 'menu.account.trigger': '触发报错',
'menu.account.logout': '退出登录', 'menu.account.logout': '退出登录',

View File

@ -9,6 +9,8 @@ export default {
'app.settings.basic.email-message': '请输入您的邮箱!', 'app.settings.basic.email-message': '请输入您的邮箱!',
'app.settings.basic.nickname': '昵称', 'app.settings.basic.nickname': '昵称',
'app.settings.basic.nickname-message': '请输入您的昵称!', 'app.settings.basic.nickname-message': '请输入您的昵称!',
'app.settings.basic.realname': '姓名',
'app.settings.basic.realname-message': '请输入您的姓名!',
'app.settings.basic.profile': '个人简介', 'app.settings.basic.profile': '个人简介',
'app.settings.basic.profile-message': '请输入个人简介!', 'app.settings.basic.profile-message': '请输入个人简介!',
'app.settings.basic.profile-placeholder': '个人简介', 'app.settings.basic.profile-placeholder': '个人简介',
@ -21,6 +23,12 @@ export default {
'app.settings.basic.phone': '联系电话', 'app.settings.basic.phone': '联系电话',
'app.settings.basic.phone-message': '请输入您的联系电话!', 'app.settings.basic.phone-message': '请输入您的联系电话!',
'app.settings.basic.update': '更新基本信息', 'app.settings.basic.update': '更新基本信息',
'app.settings.password.old': '旧密码',
'app.settings.password.old-message': '请输入你的旧密码!',
'app.settings.password.new': '新密码',
'app.settings.password.new-message': '请输入你的新密码!',
'app.settings.password.new1': '确认密码',
'app.settings.password.new1-message': '请输入你的确认密码!',
'app.settings.security.strong': '强', 'app.settings.security.strong': '强',
'app.settings.security.medium': '中', 'app.settings.security.medium': '中',
'app.settings.security.weak': '弱', 'app.settings.security.weak': '弱',

View File

@ -35,6 +35,7 @@ export default {
'menu.exception.trigger': '觸發錯誤', 'menu.exception.trigger': '觸發錯誤',
'menu.account': '個人頁', 'menu.account': '個人頁',
'menu.account.center': '個人中心', 'menu.account.center': '個人中心',
'menu.account.password': '密碼修改',
'menu.account.settings': '個人設置', 'menu.account.settings': '個人設置',
'menu.account.trigger': '觸發報錯', 'menu.account.trigger': '觸發報錯',
'menu.account.logout': '退出登錄', 'menu.account.logout': '退出登錄',

View File

@ -9,6 +9,8 @@ export default {
'app.settings.basic.email-message': '請輸入您的郵箱!', 'app.settings.basic.email-message': '請輸入您的郵箱!',
'app.settings.basic.nickname': '昵稱', 'app.settings.basic.nickname': '昵稱',
'app.settings.basic.nickname-message': '請輸入您的昵稱!', 'app.settings.basic.nickname-message': '請輸入您的昵稱!',
'app.settings.basic.realname': '姓名',
'app.settings.basic.realname-message': '請輸入您的姓名!',
'app.settings.basic.profile': '個人簡介', 'app.settings.basic.profile': '個人簡介',
'app.settings.basic.profile-message': '請輸入個人簡介!', 'app.settings.basic.profile-message': '請輸入個人簡介!',
'app.settings.basic.profile-placeholder': '個人簡介', 'app.settings.basic.profile-placeholder': '個人簡介',
@ -21,6 +23,12 @@ export default {
'app.settings.basic.phone': '聯系電話', 'app.settings.basic.phone': '聯系電話',
'app.settings.basic.phone-message': '請輸入您的聯系電話!', 'app.settings.basic.phone-message': '請輸入您的聯系電話!',
'app.settings.basic.update': '更新基本信息', 'app.settings.basic.update': '更新基本信息',
'app.settings.password.old': '舊密碼',
'app.settings.password.old-message': '請輸入妳的舊密碼!',
'app.settings.password.new': '新密碼',
'app.settings.password.new-message': '請輸入妳的新密碼!',
'app.settings.password.new1': '確認密碼',
'app.settings.password.new1-message': '請輸入妳的確認密碼!',
'app.settings.security.strong': '強', 'app.settings.security.strong': '強',
'app.settings.security.medium': '中', 'app.settings.security.medium': '中',
'app.settings.security.weak': '弱', 'app.settings.security.weak': '弱',

View File

@ -1,106 +1,161 @@
import React, { Component, Fragment } from 'react'; import React, { Component } from 'react';
import { formatMessage, FormattedMessage } from 'umi/locale'; import { formatMessage } from 'umi/locale';
import { Form, Input, Upload, Select, Button } from 'antd'; import { Form, Input, Upload, Button, message, Icon, Card } from 'antd';
import { connect } from 'dva'; import Panel from '../../../components/Panel';
import styles from './BaseView.less'; import { getUserInfo, update } from '../../../services/user';
import GeographicView from './GeographicView'; import { getToken } from '../../../utils/authority';
import PhoneView from './PhoneView';
// import { getTimeDistance } from '@/utils/utils';
const FormItem = Form.Item; const FormItem = Form.Item;
const { Option } = Select;
// 头像组件 方便以后独立,增加裁剪之类的功能
const AvatarView = ({ avatar }) => (
<Fragment>
<div className={styles.avatar_title}>
<FormattedMessage id="app.settings.basic.avatar" defaultMessage="Avatar" />
</div>
<div className={styles.avatar}>
<img src={avatar} alt="avatar" />
</div>
<Upload fileList={[]}>
<div className={styles.button_view}>
<Button icon="upload">
<FormattedMessage id="app.settings.basic.change-avatar" defaultMessage="Change avatar" />
</Button>
</div>
</Upload>
</Fragment>
);
const validatorGeographic = (rule, value, callback) => {
const { province, city } = value;
if (!province.key) {
callback('Please input your province!');
}
if (!city.key) {
callback('Please input your city!');
}
callback();
};
const validatorPhone = (rule, value, callback) => {
const values = value.split('-');
if (!values[0]) {
callback('Please input your area code!');
}
if (!values[1]) {
callback('Please input your phone number!');
}
callback();
};
@connect(({ user }) => ({
currentUser: user.currentUser,
}))
@Form.create() @Form.create()
class BaseView extends Component { class BaseView extends Component {
state = {
userId: '',
avatar: '',
loading: false,
};
componentDidMount() { componentDidMount() {
this.setBaseInfo(); this.setBaseInfo();
} }
setBaseInfo = () => { setBaseInfo = () => {
const { currentUser, form } = this.props; const { form } = this.props;
Object.keys(form.getFieldsValue()).forEach(key => { getUserInfo().then(resp => {
const obj = {}; if (resp.success) {
obj[key] = currentUser[key] || null; const userInfo = resp.data;
form.setFieldsValue(obj); Object.keys(form.getFieldsValue()).forEach(key => {
const obj = {};
obj[key] = userInfo[key] || null;
form.setFieldsValue(obj);
});
this.setState({ userId: userInfo.id, avatar: userInfo.avatar });
} else {
message.error(resp.msg || '获取数据失败');
}
}); });
}; };
getAvatarURL() { beforeUpload = file => {
const { currentUser } = this.props; const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (currentUser.avatar) { if (!isJpgOrPng) {
return currentUser.avatar; message.error('You can only upload JPG/PNG file!');
} }
const url = 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png'; const isLt2M = file.size / 1024 / 1024 < 2;
return url; if (!isLt2M) {
} message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
};
getViewDom = ref => { handleChange = info => {
this.view = ref; if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
this.setState({ loading: false, avatar: info.file.response.data.link });
}
};
handleSubmit = e => {
e.preventDefault();
const { form } = this.props;
const { userId, avatar } = this.state;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const params = {
id: userId,
...values,
avatar,
};
update(params).then(resp => {
if (resp.success) {
message.success(resp.msg);
} else {
message.error(resp.msg || '提交失败');
}
});
}
});
}; };
render() { render() {
const { const {
form: { getFieldDecorator }, form: { getFieldDecorator },
} = this.props; } = this.props;
const { avatar, loading } = this.state;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
md: { span: 10 },
},
};
const uploadProp = {
action: '/api/blade-resource/oss/endpoint/put-file',
headers: {
'Blade-Auth': getToken(),
},
};
const uploadButton = (
<div>
<Icon type={loading ? 'loading' : 'plus'} />
<div className="ant-upload-text">上传头像</div>
</div>
);
const action = (
<Button type="primary" onClick={this.handleSubmit}>
提交
</Button>
);
return ( return (
<div className={styles.baseView} ref={this.getViewDom}> <Panel title="个人设置" back="/" action={action}>
<div className={styles.left}> <Form style={{ marginTop: 8 }} hideRequiredMark>
<Form layout="vertical" onSubmit={this.handleSubmit} hideRequiredMark> <Card title="基本信息" bordered={false}>
<FormItem label={formatMessage({ id: 'app.settings.basic.email' })}> <FormItem
{getFieldDecorator('email', { {...formItemLayout}
label={formatMessage({ id: 'app.settings.basic.avatar' })}
>
{getFieldDecorator('avatar', {
rules: [ rules: [
{ {
required: true, required: true,
message: formatMessage({ id: 'app.settings.basic.email-message' }, {}), message: formatMessage({ id: 'app.settings.basic.avatar' }, {}),
}, },
], ],
})(<Input />)} })(
<Upload
name="file"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
beforeUpload={this.beforeUpload}
onChange={this.handleChange}
{...uploadProp}
>
{avatar ? (
<img src={avatar} alt="avatar" style={{ width: '100%' }} />
) : (
uploadButton
)}
</Upload>
)}
</FormItem> </FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.nickname' })}> <FormItem
{...formItemLayout}
label={formatMessage({ id: 'app.settings.basic.nickname' })}
>
{getFieldDecorator('name', { {getFieldDecorator('name', {
rules: [ rules: [
{ {
@ -110,81 +165,42 @@ class BaseView extends Component {
], ],
})(<Input />)} })(<Input />)}
</FormItem> </FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.profile' })}> <FormItem
{getFieldDecorator('profile', { {...formItemLayout}
label={formatMessage({ id: 'app.settings.basic.realname' })}
>
{getFieldDecorator('realName', {
rules: [ rules: [
{ {
required: true, required: true,
message: formatMessage({ id: 'app.settings.basic.profile-message' }, {}), message: formatMessage({ id: 'app.settings.basic.realname-message' }, {}),
},
],
})(
<Input.TextArea
placeholder={formatMessage({ id: 'app.settings.basic.profile-placeholder' })}
rows={4}
/>
)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.country' })}>
{getFieldDecorator('country', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.country-message' }, {}),
},
],
})(
<Select style={{ maxWidth: 220 }}>
<Option value="China">中国</Option>
</Select>
)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.geographic' })}>
{getFieldDecorator('geographic', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.geographic-message' }, {}),
},
{
validator: validatorGeographic,
},
],
})(<GeographicView />)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.address' })}>
{getFieldDecorator('address', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.address-message' }, {}),
}, },
], ],
})(<Input />)} })(<Input />)}
</FormItem> </FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.phone' })}> <FormItem {...formItemLayout} label={formatMessage({ id: 'app.settings.basic.phone' })}>
{getFieldDecorator('phone', { {getFieldDecorator('phone', {
rules: [ rules: [
{ {
required: true, required: true,
message: formatMessage({ id: 'app.settings.basic.phone-message' }, {}), message: formatMessage({ id: 'app.settings.basic.phone-message' }, {}),
}, },
{ validator: validatorPhone },
], ],
})(<PhoneView />)} })(<Input />)}
</FormItem> </FormItem>
<Button type="primary"> <FormItem {...formItemLayout} label={formatMessage({ id: 'app.settings.basic.email' })}>
<FormattedMessage {getFieldDecorator('email', {
id="app.settings.basic.update" rules: [
defaultMessage="Update Information" {
/> required: true,
</Button> message: formatMessage({ id: 'app.settings.basic.email-message' }, {}),
</Form> },
</div> ],
<div className={styles.right}> })(<Input />)}
<AvatarView avatar={this.getAvatarURL()} /> </FormItem>
</div> </Card>
</div> </Form>
</Panel>
); );
} }
} }

View File

@ -0,0 +1,98 @@
import React, { Component } from 'react';
import { formatMessage } from 'umi/locale';
import { Form, Input, Button, Card, message } from 'antd';
import Panel from '../../../components/Panel';
import { updatePassword } from '../../../services/user';
const FormItem = Form.Item;
@Form.create()
class PasswordView extends Component {
handleSubmit = e => {
e.preventDefault();
const { form } = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
updatePassword(values).then(resp => {
if (resp.success) {
message.success(resp.msg);
}
});
}
});
};
render() {
const {
form: { getFieldDecorator },
} = 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.handleSubmit}>
提交
</Button>
);
return (
<Panel title="密码修改" back="/" action={action}>
<Form style={{ marginTop: 8 }} hideRequiredMark>
<Card title="基本信息" bordered={false}>
<FormItem
{...formItemLayout}
label={formatMessage({ id: 'app.settings.password.old' })}
>
{getFieldDecorator('oldPassword', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.password.old-message' }, {}),
},
],
})(<Input type="password" />)}
</FormItem>
<FormItem
{...formItemLayout}
label={formatMessage({ id: 'app.settings.password.new' })}
>
{getFieldDecorator('newPassword', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.password.new-message' }, {}),
},
],
})(<Input type="password" />)}
</FormItem>
<FormItem
{...formItemLayout}
label={formatMessage({ id: 'app.settings.password.new1' })}
>
{getFieldDecorator('newPassword1', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.password.new1-message' }, {}),
},
],
})(<Input type="password" />)}
</FormItem>
</Card>
</Form>
</Panel>
);
}
}
export default PasswordView;

View File

@ -103,7 +103,14 @@ class Workplace extends PureComponent {
</Collapse> </Collapse>
</Col> </Col>
<Col span={8}> <Col span={8}>
<Collapse bordered={false} defaultActiveKey={['10']}> <Collapse bordered={false} defaultActiveKey={['11']}>
<Panel header="2.5.2发布 增加个人中心" key="11">
<div>1.增加个人中心支持用户信息自定义修改</div>
<div>2.增加网关鉴权配置示例</div>
<div>3.token的SIGN_KEY修改为一致</div>
<div>4.admin模块增加对seata服务的过滤</div>
<div>5.blade-tool增加部分工具类方法</div>
</Panel>
<Panel header="2.5.1发布 增加网关动态鉴权" key="10"> <Panel header="2.5.1发布 增加网关动态鉴权" key="10">
<div>1.增加网关动态鉴权</div> <div>1.增加网关动态鉴权</div>
<div>2.secure安全模块token校验默认关闭交由网关处理</div> <div>2.secure安全模块token校验默认关闭交由网关处理</div>

View File

@ -122,6 +122,7 @@ class LoginPage extends Component {
{tenantMode ? ( {tenantMode ? (
<TenantId <TenantId
name="tenantId" name="tenantId"
defaultValue="000000"
placeholder={`${formatMessage({ id: 'app.login.tenantId' })}: 000000`} placeholder={`${formatMessage({ id: 'app.login.tenantId' })}: 000000`}
rules={[ rules={[
{ {
@ -133,6 +134,7 @@ class LoginPage extends Component {
) : null} ) : null}
<UserName <UserName
name="account" name="account"
defaultValue="admin"
placeholder={`${formatMessage({ id: 'app.login.userName' })}: admin`} placeholder={`${formatMessage({ id: 'app.login.userName' })}: admin`}
rules={[ rules={[
{ {
@ -143,6 +145,7 @@ class LoginPage extends Component {
/> />
<Password <Password
name="password" name="password"
defaultValue="admin"
placeholder={`${formatMessage({ id: 'app.login.password' })}: admin`} placeholder={`${formatMessage({ id: 'app.login.password' })}: admin`}
rules={[ rules={[
{ {

View File

@ -61,3 +61,14 @@ export async function update(params) {
export async function detail(params) { export async function detail(params) {
return request(`/api/blade-user/detail?${stringify(params)}`); return request(`/api/blade-user/detail?${stringify(params)}`);
} }
export async function getUserInfo() {
return request('/api/blade-user/info');
}
export async function updatePassword(params) {
return request('/api/blade-user/update-password', {
method: 'POST',
body: func.toFormData(params),
});
}