From 5954dac87b14cb9f20acde96f1666619be28833e Mon Sep 17 00:00:00 2001
From: Ky_Gyt <1971574843@qq.com>
Date: Wed, 30 Jun 2021 11:42:59 +0800
Subject: [PATCH] =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E8=AE=BE=E7=BD=AE=E5=92=8C?=
=?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web-react/src/common/storage/index.js | 3 +-
web-react/src/pages/system/account/index.jsx | 41 ++
.../src/pages/system/account/setting/info.jsx | 127 +++++
.../system/account/setting/satety/index.jsx | 179 +++++++
.../system/account/setting/satety/mail.jsx | 454 +++++++++++++++++
.../account/setting/satety/password.jsx | 107 ++++
.../system/account/setting/satety/phone.jsx | 455 ++++++++++++++++++
.../src/views/main/_layout/header/user.jsx | 11 +-
8 files changed, 1374 insertions(+), 3 deletions(-)
create mode 100644 web-react/src/pages/system/account/index.jsx
create mode 100644 web-react/src/pages/system/account/setting/info.jsx
create mode 100644 web-react/src/pages/system/account/setting/satety/index.jsx
create mode 100644 web-react/src/pages/system/account/setting/satety/mail.jsx
create mode 100644 web-react/src/pages/system/account/setting/satety/password.jsx
create mode 100644 web-react/src/pages/system/account/setting/satety/phone.jsx
diff --git a/web-react/src/common/storage/index.js b/web-react/src/common/storage/index.js
index 73e8b15..444d5c1 100644
--- a/web-react/src/common/storage/index.js
+++ b/web-react/src/common/storage/index.js
@@ -1,9 +1,10 @@
const SESSION_KEY = '__SESSION'
const SETTING_KEY = '__SETTINGS'
const GLOBAL_INFO_KEY = '__GLOBAL_INFO'
-
+const COUNT_DWON_KEY = '__COUNT_DWON'
export {
SESSION_KEY,
SETTING_KEY,
GLOBAL_INFO_KEY,
+ COUNT_DWON_KEY
}
\ No newline at end of file
diff --git a/web-react/src/pages/system/account/index.jsx b/web-react/src/pages/system/account/index.jsx
new file mode 100644
index 0000000..4492f45
--- /dev/null
+++ b/web-react/src/pages/system/account/index.jsx
@@ -0,0 +1,41 @@
+import React, { Component } from 'react'
+import { Anchor, Form, Input, InputNumber, Spin } from 'antd'
+import { AntIcon, Container, IconSelector } from 'components'
+import { cloneDeep } from 'lodash'
+import Safety from './setting/satety/index'
+import Info from './setting/info'
+import nav from 'store/reducer/nav'
+
+export default class index extends Component {
+ state = {}
+
+ render() {
+ // let navs = [
+ // {
+ // title: '我的信息',
+ // component: require('./setting/info'),
+ // },
+ // {
+ // title: '安全设置',
+ // component: require('./setting/satety'),
+ // },
+ // ]
+
+ // return (
+ //
+ //
+ // {navs.map(item => {
+ // return
+ // })}
+ //
+ //
+ //
+ // )
+ return (
+
+
+
+
+ )
+ }
+}
diff --git a/web-react/src/pages/system/account/setting/info.jsx b/web-react/src/pages/system/account/setting/info.jsx
new file mode 100644
index 0000000..211d2e1
--- /dev/null
+++ b/web-react/src/pages/system/account/setting/info.jsx
@@ -0,0 +1,127 @@
+import React, { Component } from 'react'
+import { Button, DatePicker, Form, Input, message, Radio, Spin } from 'antd'
+import { api } from 'common/api'
+import { cloneDeep } from 'lodash'
+import { AntIcon, Container, IconSelector, Image } from 'components'
+import store from 'store'
+import moment from 'moment'
+
+const { getState } = store
+
+export default class index extends Component {
+ state = {
+ info: getState('user'),
+ saving: false,
+ loading: false,
+ }
+ form = React.createRef()
+ componentDidMount() {
+ this.setState({
+ loading: true,
+ })
+ api.getLoginUser()
+ .then(({ data }) => {
+ delete data.apps
+ delete data.menus
+ this.setState({
+ info: data,
+ })
+ let birthday = data.birthday
+ birthday = moment(birthday)
+ data = {
+ ...data,
+ birthday: birthday,
+ }
+ this.form.current.setFieldsValue(data)
+ })
+ .finally(() => {
+ this.setState({
+ loading: false,
+ })
+ })
+ }
+ onSvaeInfo(data) {
+ this.setState({
+ saving: true,
+ })
+ let { birthday } = data.current.getFieldsValue()
+ let { nickName } = data.current.getFieldsValue()
+ let { sex } = data.current.getFieldsValue()
+ let { tel } = data.current.getFieldsValue()
+ api.sysUserUpdateInfo({
+ nickName: nickName,
+ birthday: birthday,
+ sex: sex,
+ tel: tel,
+ }).then(() => {
+ message.success('更新个人信息成功')
+ this.setState({
+ saving: false,
+ })
+ })
+ }
+ onAvatarStart() {}
+
+ render() {
+ const { info } = this.state
+ return (
+
+ }>
+
+
+
+
+ )
+ }
+}
diff --git a/web-react/src/pages/system/account/setting/satety/index.jsx b/web-react/src/pages/system/account/setting/satety/index.jsx
new file mode 100644
index 0000000..df20c83
--- /dev/null
+++ b/web-react/src/pages/system/account/setting/satety/index.jsx
@@ -0,0 +1,179 @@
+import React, { Component } from 'react'
+import { Button, DatePicker, Form, Input, List, message as Message, Spin } from 'antd'
+import { api } from 'common/api'
+import { cloneDeep } from 'lodash'
+import { AntIcon, Container, IconSelector, Image, ModalForm } from 'components'
+import store from 'store'
+import moment from 'moment'
+import Item from 'antd/lib/list/Item'
+import PasswordForm from './password'
+import Mail from './mail'
+import Phone from './phone'
+const { getState } = store
+const apiAction = {
+ update: api.sysUserUpdatePwd,
+}
+
+export default class form extends Component {
+ state = {
+ saving: false,
+ info: [],
+ loading: true,
+ }
+ form = React.createRef()
+ // 新增窗口实例
+ updateForm = React.createRef()
+ MailForm = React.createRef()
+ PhoneForm = React.createRef()
+ /**
+ * 对表格上的操作进行统一处理
+ * [异步]
+ * @param {*} action
+ * @param {*} successMessage
+ */
+ async onAction(action, successMessage) {
+ const { onLoading, onLoaded, onReloadData } = this.table.current
+ onLoading()
+ try {
+ if (action) {
+ await action
+ }
+ if (successMessage) {
+ Message.success(successMessage)
+ }
+ onReloadData()
+ } catch {
+ onLoaded()
+ }
+ }
+
+ /**
+ * 打开新增/编辑弹窗
+ * @param {*} modal
+ * @param {*} record
+ */
+ onOpen(modal, record) {
+ modal.current.open({
+ record,
+ })
+ }
+
+ componentDidMount() {
+ api.getLoginUser().then(({ data }) => {
+ this.setState({
+ loading: true,
+ })
+ let index = []
+ //密码
+ index.push({
+ title: '登录密码',
+ description:
+ '安全性高的密码可以使帐号更安全。建议您定期更换密码,设置一个包含字母,符号或数字中至少两项且长度超过6位的密码。',
+ extra: (
+
+ 当前密码强度为:
+ {
+ [
+ 弱,
+ 中,
+ 强,
+ ][data.securityLevel - 1]
+ }
+
+ ),
+ done: true,
+ action: () => {
+ this.onOpen(this.updateForm)
+ },
+ })
+ //手机
+ index.push({
+ title: '手机绑定(发送验证码到手机,未实现)',
+ description: (
+
+ 手机号可以直接用于登录、找回密码等。
+ {data.phone && (
+
+ 您已绑定了手机{data.phone}
+
+ )}
+
+ ),
+ done: !!data.phone,
+ action: () => {
+ this.onOpen(this.PhoneForm)
+ },
+ })
+ //邮箱
+ index.push({
+ title: '邮箱绑定(发送验证码到邮箱,未实现)',
+ description: (
+
+ 安全邮箱可以直接用于登录、找回密码等。
+ {data.email && (
+
+ 您已绑定了邮箱{data.email}
+
+ )}
+
+ ),
+ done: !!data.email,
+ action: () => {
+ this.onOpen(this.MailForm)
+ },
+ })
+ this.setState({
+ info: index,
+ loading: false,
+ })
+ })
+ }
+ render() {
+ return (
+
+ }>
+
+
安全设置
+
+ (
+
+ {item.done == true ? (
+ <>
+
+
+ 已设置
+
+
+ 修改
+
+ >
+ ) : (
+ <>
+
+
+ 未设置
+
+
+ 设置
+
+ >
+ )}
+
+
+ )}
+ />
+
+
+
+
+
+
+
+
+ )
+ }
+}
diff --git a/web-react/src/pages/system/account/setting/satety/mail.jsx b/web-react/src/pages/system/account/setting/satety/mail.jsx
new file mode 100644
index 0000000..e391cc0
--- /dev/null
+++ b/web-react/src/pages/system/account/setting/satety/mail.jsx
@@ -0,0 +1,454 @@
+import React, { Component } from 'react'
+import {
+ Form,
+ Input,
+ InputNumber,
+ Modal,
+ Spin,
+ Steps,
+ Button,
+ Row,
+ Col,
+ message,
+ Select,
+} from 'antd'
+import { AntIcon, Container, IconSelector } from 'components'
+import { cloneDeep, indexOf } from 'lodash'
+import { api } from 'common/api'
+import { COUNT_DWON_KEY } from 'common/storage'
+import { Option } from 'antd/lib/mentions'
+import { set } from 'nprogress'
+import { getKeyThenIncreaseKey } from 'antd/lib/message'
+
+const initialValues = {
+ orgcode: '',
+ target: '',
+ code: '',
+ type: null,
+}
+var tempcode = ''
+const { Step } = Steps
+export default class form extends Component {
+ state = {
+ buttondisabled: true,
+ visible: false,
+ loading: false,
+ codeLoading: false,
+ current: 0,
+ countdown: 0,
+ sendOrNo: true,
+ type: [],
+ }
+ form = React.createRef()
+ //发送验证码
+ sendcode(data) {
+ this.setState({
+ codeLoading: true,
+ })
+ var reg = /^([a-zA-Z]|[0-9])(\w|)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/
+ let { target } = data.current.getFieldsValue()
+ let { type } = data.current.getFieldsValue()
+ let { code } = data.current.getFieldsValue()
+ let { orgcode } = data.current.getFieldsValue()
+ if (!reg.test(target) && type != '1' && type != '2') {
+ message.warn('请输入正确的邮箱')
+ this.setState({
+ codeLoading: true,
+ })
+ return
+ }
+ api.SendCode({
+ target: target,
+ type: type,
+ code: code,
+ orgcode: orgcode,
+ })
+ .then(res => {
+ if (res.success) {
+ this.addTime()
+ this.showcountdown()
+ }
+ })
+ .finally(() => {
+ this.setState({
+ codeLoading: false,
+ })
+ })
+ }
+ //进入下一步
+ next(data) {
+ this.setState({
+ loading: true,
+ })
+ let { target } = data.current.getFieldsValue()
+ let { type } = data.current.getFieldsValue()
+ let { code } = data.current.getFieldsValue()
+ let { orgcode } = data.current.getFieldsValue()
+ tempcode = data.current.getFieldsValue()
+ let form = {
+ target: target,
+ type: type,
+ code: code,
+ orgcode: orgcode,
+ }
+ api.CheckBindcode(form)
+ .then(res => {
+ if (res.data) {
+ window.localStorage.removeItem(COUNT_DWON_KEY)
+ let current = this.state.current + 1
+ this.setState({
+ form: {
+ ...form,
+ type: null,
+ },
+ buttondisabled: true,
+ current: current,
+ })
+ }
+ })
+ .finally(() => {
+ this.setState({
+ loading: false,
+ })
+ })
+ }
+ /**
+ * 将倒计时添加入到本地
+ */
+ addTime() {
+ const now = Date.now()
+ var date = now + 60 * 1000 + 500
+ window.localStorage.setItem(COUNT_DWON_KEY, date)
+ }
+ /**
+ * 显示倒计时
+ */
+ showcountdown() {
+ let _this = this
+ var Furdate = window.localStorage.getItem(COUNT_DWON_KEY)
+ var nowdate = new Date().getTime()
+ if (Furdate >= nowdate) {
+ this.setState({
+ sendOrNo: false,
+ countdown: parseInt((Furdate - nowdate) / 1000),
+ })
+ setTimeout(() => {
+ _this.showcountdown()
+ }, 1000)
+ } else {
+ this.setState({
+ sendOrNo: true,
+ })
+ }
+ }
+ //打开窗口
+ open = (data = {}) => {
+ this.setState({ visible: true, loading: true })
+ api.getLoginUser().then(({ data }) => {
+ let index = []
+ data.phone &&
+ index.push({
+ Title: '使用手机号' + data.phone + '进行验证 ',
+ Value: 1,
+ })
+ data.email &&
+ index.push({
+ Title: '使用邮箱' + data.email + '进行验证',
+ Value: 2,
+ })
+ this.setState({
+ type: index,
+ })
+ if (index.length > 0) {
+ this.form.current.setFieldsValue({
+ type: index[0].Value,
+ })
+ }
+ this.setState({ loading: false })
+ })
+ }
+ // 前一步
+ prev() {
+ window.localStorage.removeItem(COUNT_DWON_KEY)
+ let current = this.state.current - 1
+ this.setState({
+ current: current,
+ })
+ }
+ //完成
+ complete(data) {
+ let { target } = data.current.getFieldsValue()
+ let { code } = data.current.getFieldsValue()
+ let { orgcode } = tempcode
+ api.CheckBindcode({
+ target: target,
+ type: null,
+ code: code,
+ orgcode: orgcode,
+ }).then(res => {
+ if (res.data) {
+ window.localStorage.removeItem(COUNT_DWON_KEY)
+ message.success('改绑完成')
+ this.onResetFields()
+ }
+ })
+ }
+ onResetFields() {
+ setTimeout(() => {
+ this.setState({ visible: false })
+ this.setState({ current: 0 })
+ //window.localStorage.removeItem(COUNT_DWON_KEY)
+ /** 在这里可以初始化当前组件中其他属性 */
+ /* ... */
+ }, 300)
+ }
+ render() {
+ let steps = [
+ {
+ title: '验证',
+ },
+ {
+ title: '绑定',
+ },
+ ]
+ const close = () => {
+ this.setState({
+ visible: false,
+ current: 0,
+ })
+ }
+ return (
+
+
+ }>
+
+ {this.state.type.length !== 0 ? (
+
+
+
+
+
+
+ {steps.map(item => (
+
+ ))}
+
+
+
+
+
+
+
+
+ {this.state.current == 0 && (
+ <>
+
+
+
+
+ >
+ )}
+ {this.state.current == 1 && (
+ <>
+ {this.state.current > 0 && (
+
+ )}
+
+ >
+ )}
+
+
+ ) : (
+
+
+
+
+
+
+
+
+
+
+ {this.state.sendOrNo ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
+
+
+ )
+ }
+}
diff --git a/web-react/src/pages/system/account/setting/satety/password.jsx b/web-react/src/pages/system/account/setting/satety/password.jsx
new file mode 100644
index 0000000..0616fc1
--- /dev/null
+++ b/web-react/src/pages/system/account/setting/satety/password.jsx
@@ -0,0 +1,107 @@
+import React, { Component } from 'react'
+import { Form, Input, InputNumber, Spin } from 'antd'
+import { AntIcon, IconSelector } from 'components'
+import { cloneDeep } from 'lodash'
+
+const initialValues = {
+ sort: 100,
+}
+export default class form extends Component {
+ state = {
+ // 加载状态
+ loading: true,
+ exist: false,
+ }
+ // 表单实例
+ form = React.createRef()
+
+ // 初始化数据
+ record = {}
+
+ /**
+ * mount后回调
+ */
+ componentDidMount() {
+ this.props.created && this.props.created(this)
+ }
+
+ /**
+ * 填充数据
+ * 可以在设置this.record之后对其作出数据结构调整
+ * [异步,必要]
+ * @param {*} params
+ */
+ async fillData(params) {
+ this.record = cloneDeep(params.record)
+ //#region 从后端转换成前段所需格式
+ const exist = !!params.record
+ this.setState({
+ exist,
+ })
+
+ this.record = {
+ ...this.record,
+ }
+ //#endregion
+
+ this.form.current.setFieldsValue(this.record)
+
+ this.setState({
+ loading: false,
+ })
+ }
+
+ /**
+ * 获取数据
+ * 可以对postData进行数据结构调整
+ * [异步,必要]
+ * @returns
+ */
+ async getData() {
+ const form = this.form.current
+
+ const valid = await form.validateFields()
+ if (valid) {
+ const postData = form.getFieldsValue()
+ if (this.record) {
+ postData.id = this.record.id
+ }
+ //#region 从前段转换后端所需格式
+ //#endregion
+ return postData
+ }
+ }
+
+ render() {
+ return (
+
+ )
+ }
+}
diff --git a/web-react/src/pages/system/account/setting/satety/phone.jsx b/web-react/src/pages/system/account/setting/satety/phone.jsx
new file mode 100644
index 0000000..d5ebb56
--- /dev/null
+++ b/web-react/src/pages/system/account/setting/satety/phone.jsx
@@ -0,0 +1,455 @@
+import React, { Component } from 'react'
+import {
+ Form,
+ Input,
+ InputNumber,
+ Modal,
+ Spin,
+ Steps,
+ Button,
+ Row,
+ Col,
+ message,
+ Select,
+} from 'antd'
+import { AntIcon, Container, IconSelector } from 'components'
+import { cloneDeep, indexOf } from 'lodash'
+import { api } from 'common/api'
+import { COUNT_DWON_KEY } from 'common/storage'
+import { Option } from 'antd/lib/mentions'
+import { set } from 'nprogress'
+import { getKeyThenIncreaseKey } from 'antd/lib/message'
+
+const initialValues = {
+ orgcode: '',
+ target: '',
+ code: '',
+ type: null,
+}
+var tempcode = ''
+const { Step } = Steps
+export default class form extends Component {
+ state = {
+ buttondisabled: true,
+ visible: false,
+ loading: false,
+ codeLoading: false,
+ current: 0,
+ countdown: 0,
+ sendOrNo: true,
+ type: [],
+ }
+ form = React.createRef()
+ //发送验证码
+ sendcode(data) {
+ this.setState({
+ codeLoading: true,
+ })
+ var reg =
+ /^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$/
+ let { target } = data.current.getFieldsValue()
+ let { type } = data.current.getFieldsValue()
+ let { code } = data.current.getFieldsValue()
+ let { orgcode } = data.current.getFieldsValue()
+ if (!reg.test(target) && type != '1' && type != '2') {
+ message.warn('请输入正确的手机号码')
+ this.setState({
+ codeLoading: true,
+ })
+ return
+ }
+ api.SendCode({
+ target: target,
+ type: type,
+ code: code,
+ orgcode: orgcode,
+ })
+ .then(res => {
+ if (res.success) {
+ this.addTime()
+ this.showcountdown()
+ }
+ })
+ .finally(() => {
+ this.setState({
+ codeLoading: false,
+ })
+ })
+ }
+ //进入下一步
+ next(data) {
+ this.setState({
+ loading: true,
+ })
+ let { target } = data.current.getFieldsValue()
+ let { type } = data.current.getFieldsValue()
+ let { code } = data.current.getFieldsValue()
+ let { orgcode } = data.current.getFieldsValue()
+ tempcode = data.current.getFieldsValue()
+ let form = {
+ target: target,
+ type: type,
+ code: code,
+ orgcode: orgcode,
+ }
+ api.CheckBindcode(form)
+ .then(res => {
+ if (res.data) {
+ window.localStorage.removeItem(COUNT_DWON_KEY)
+ let current = this.state.current + 1
+ this.setState({
+ form: {
+ ...form,
+ type: null,
+ },
+ buttondisabled: true,
+ current: current,
+ })
+ }
+ })
+ .finally(() => {
+ this.setState({
+ loading: false,
+ })
+ })
+ }
+ /**
+ * 将倒计时添加入到本地
+ */
+ addTime() {
+ const now = Date.now()
+ var date = now + 60 * 1000 + 500
+ window.localStorage.setItem(COUNT_DWON_KEY, date)
+ }
+ /**
+ * 显示倒计时
+ */
+ showcountdown() {
+ let _this = this
+ var Furdate = window.localStorage.getItem(COUNT_DWON_KEY)
+ var nowdate = new Date().getTime()
+ if (Furdate >= nowdate) {
+ this.setState({
+ sendOrNo: false,
+ countdown: parseInt((Furdate - nowdate) / 1000),
+ })
+ setTimeout(() => {
+ _this.showcountdown()
+ }, 1000)
+ } else {
+ this.setState({
+ sendOrNo: true,
+ })
+ }
+ }
+ //打开窗口
+ open = (data = {}) => {
+ this.setState({ visible: true, loading: true })
+ api.getLoginUser().then(({ data }) => {
+ let index = []
+ data.phone &&
+ index.push({
+ Title: '使用手机号' + data.phone + '进行验证 ',
+ Value: 1,
+ })
+ data.email &&
+ index.push({
+ Title: '使用邮箱' + data.email + '进行验证',
+ Value: 2,
+ })
+ this.setState({
+ type: index,
+ })
+ if (index.length > 0) {
+ this.form.current.setFieldsValue({
+ type: index[0].Value,
+ })
+ }
+ this.setState({ loading: false })
+ })
+ }
+ // 前一步
+ prev() {
+ window.localStorage.removeItem(COUNT_DWON_KEY)
+ let current = this.state.current - 1
+ this.setState({
+ current: current,
+ })
+ }
+ //完成
+ complete(data) {
+ let { target } = data.current.getFieldsValue()
+ let { code } = data.current.getFieldsValue()
+ let { orgcode } = tempcode
+ api.CheckBindcode({
+ target: target,
+ type: null,
+ code: code,
+ orgcode: orgcode,
+ }).then(res => {
+ if (res.data) {
+ window.localStorage.removeItem(COUNT_DWON_KEY)
+ message.success('改绑完成')
+ this.onResetFields()
+ }
+ })
+ }
+ onResetFields() {
+ setTimeout(() => {
+ this.setState({ visible: false })
+ this.setState({ current: 0 })
+ //window.localStorage.removeItem(COUNT_DWON_KEY)
+ /** 在这里可以初始化当前组件中其他属性 */
+ /* ... */
+ }, 300)
+ }
+ render() {
+ let steps = [
+ {
+ title: '验证',
+ },
+ {
+ title: '绑定',
+ },
+ ]
+ const close = () => {
+ this.setState({
+ visible: false,
+ current: 0,
+ })
+ }
+ return (
+
+
+ }>
+
+ {this.state.type.length !== 0 ? (
+
+
+
+
+
+
+ {steps.map(item => (
+
+ ))}
+
+
+
+
+
+
+
+
+ {this.state.current == 0 && (
+ <>
+
+
+
+
+ >
+ )}
+ {this.state.current == 1 && (
+ <>
+ {this.state.current > 0 && (
+
+ )}
+
+ >
+ )}
+
+
+ ) : (
+
+
+
+
+
+
+
+
+
+
+ {this.state.sendOrNo ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
+
+
+ )
+ }
+}
diff --git a/web-react/src/views/main/_layout/header/user.jsx b/web-react/src/views/main/_layout/header/user.jsx
index b126243..cca8939 100644
--- a/web-react/src/views/main/_layout/header/user.jsx
+++ b/web-react/src/views/main/_layout/header/user.jsx
@@ -34,7 +34,14 @@ class User extends Component {
this.unsubscribe()
}
- onAccountSetting = () => {}
+ onAccountSetting = () => {
+ window.openContentWindow({
+ id: 'account-home',
+ title: '个人设置',
+ icon: '',
+ path: '/system/account',
+ })
+ }
onLogout = () => {
Modal.confirm({
@@ -83,7 +90,7 @@ class User extends Component {