From 8d3352a55a154a67292f6d159fd798ce1480f569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=AA=E5=B8=A6=E5=A4=A7=E4=BD=AC=E6=B0=94=E5=9C=BA?= <188633308@qq.com> Date: Wed, 30 Jun 2021 20:16:35 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E4=B8=AA=E4=BA=BA=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Api/Ewide.Core/Service/User/SysUserService.cs | 4 +- web-react/package.json | 2 + web-react/src/assets/style/lib/anchor.less | 3 - web-react/src/assets/style/lib/form.less | 17 + .../src/common/api/requests/sys/userManage.js | 4 +- web-react/src/components/container/index.jsx | 26 +- web-react/src/components/image/index.jsx | 17 +- web-react/src/components/index.js | 2 +- web-react/src/pages/system/account/base.jsx | 186 +++++ web-react/src/pages/system/account/base.less | 51 ++ web-react/src/pages/system/account/index.jsx | 132 ++- .../src/pages/system/account/setting/info.jsx | 172 ++-- .../system/account/setting/satety/index.jsx | 259 +++--- .../system/account/setting/satety/mail.jsx | 756 ++++++++--------- .../account/setting/satety/password.jsx | 90 +-- .../system/account/setting/satety/phone.jsx | 757 ++++++++---------- .../src/views/main/_layout/header/user.jsx | 7 +- web-react/yarn.lock | 12 + 18 files changed, 1277 insertions(+), 1220 deletions(-) create mode 100644 web-react/src/pages/system/account/base.jsx create mode 100644 web-react/src/pages/system/account/base.less diff --git a/Api/Ewide.Core/Service/User/SysUserService.cs b/Api/Ewide.Core/Service/User/SysUserService.cs index 01e6950..f84661f 100644 --- a/Api/Ewide.Core/Service/User/SysUserService.cs +++ b/Api/Ewide.Core/Service/User/SysUserService.cs @@ -466,7 +466,7 @@ namespace Ewide.Core.Service /// ///发送验证码 /// - [HttpPost("/sysUser/SendCode")] + [HttpPost("/sysUser/sendCode")] public async Task SendCode(Usermailphone input) { var Orgcode_Key = "ewide_Orgcode"; @@ -540,7 +540,7 @@ namespace Ewide.Core.Service /// ///检验验证码并且绑定 /// - [HttpPost("/sysUser/CheckBindcode")] + [HttpPost("/sysUser/checkBindcode")] public async Task CheckBindcode(Usermailphone input) { var Orgcode_Key = "ewide_Orgcode"; diff --git a/web-react/package.json b/web-react/package.json index 8f2e44b..3a99c4e 100644 --- a/web-react/package.json +++ b/web-react/package.json @@ -20,6 +20,7 @@ "photoswipe": "^4.1.3", "react": "^17.0.2", "react-color": "^2.19.3", + "react-cropper": "^2.1.8", "react-dom": "^17.0.2", "react-json-view": "^1.21.3", "react-monaco-editor": "^0.43.0", @@ -44,6 +45,7 @@ "rules": { "eqeqeq": "off", "no-unused-vars": "off", + "no-sparse-arrays": "off", "array-callback-return": "off", "jsx-a11y/anchor-is-valid": "off" } diff --git a/web-react/src/assets/style/lib/anchor.less b/web-react/src/assets/style/lib/anchor.less index 198ee0d..e37e90f 100644 --- a/web-react/src/assets/style/lib/anchor.less +++ b/web-react/src/assets/style/lib/anchor.less @@ -9,6 +9,3 @@ border-radius: 0; background-color: @primary-color; } -.ant-anchor-link-active { - background: linear-gradient(90deg, #fff, transparent); -} diff --git a/web-react/src/assets/style/lib/form.less b/web-react/src/assets/style/lib/form.less index c587a27..4efbe7a 100644 --- a/web-react/src/assets/style/lib/form.less +++ b/web-react/src/assets/style/lib/form.less @@ -220,6 +220,23 @@ color: fade(@black, 50%); } } + &.yo-form--no-border { + .ant-form-item { + padding: @padding-md 0; + + border-right: 0; + border-left: 0; + &:first-child { + border-top: 0; + } + &:last-child { + border-bottom: 0; + } + } + .yo-form-group { + margin-bottom: 0; + } + } } .yo-modal-form { .ant-modal-body { diff --git a/web-react/src/common/api/requests/sys/userManage.js b/web-react/src/common/api/requests/sys/userManage.js index af29f3f..2d9a478 100644 --- a/web-react/src/common/api/requests/sys/userManage.js +++ b/web-react/src/common/api/requests/sys/userManage.js @@ -78,12 +78,12 @@ const urls = { /** * 发送验证码 */ - SendCode: ['/sysUser/SendCode', 'post'], + sysUserSendCode: ['/sysUser/sendCode', 'post'], /** * 绑定/验证 */ - CheckBindcode: ['/sysUser/CheckBindcode', 'post'], + sysUserCheckBindcode: ['/sysUser/checkBindcode', 'post'], } diff --git a/web-react/src/components/container/index.jsx b/web-react/src/components/container/index.jsx index d3c57fb..c2edbdc 100644 --- a/web-react/src/components/container/index.jsx +++ b/web-react/src/components/container/index.jsx @@ -1,29 +1,23 @@ import React, { Component } from 'react' export default class Container extends Component { - getMode(mode) { const c = 'container' - switch (mode) { - case 'sm': - return c + '-sm' - case 'md': - return c + '-md' - case 'fluid': - return c + '-fluid' - default: - return c + const modes = ['xxs', 'xs', 'sm', 'md', 'fluid'] + if (modes.includes(mode)) { + return `${c}-${mode}` } + return c } render() { - let className = this.getMode(this.props.mode) - if (this.props.className) { - className += ` ${this.props.className}` + const { mode, className, children } = this.props + + let containerName = this.getMode(mode) + if (className) { + containerName = [containerName, className].join(' ') } - return ( -
{this.props.children}
- ) + return
{children}
} } diff --git a/web-react/src/components/image/index.jsx b/web-react/src/components/image/index.jsx index 2b2c6c5..d84dc6a 100644 --- a/web-react/src/components/image/index.jsx +++ b/web-react/src/components/image/index.jsx @@ -3,7 +3,7 @@ import { Avatar } from 'antd' import { isEqual } from 'lodash' import { PreviewFileBase64 } from 'util/file' -const getSrc = async (id) => { +const getSrc = async id => { if (id) { const base64 = await PreviewFileBase64(id) return base64 @@ -12,9 +12,8 @@ const getSrc = async (id) => { } export default class Image extends Component { - state = { - src: '' + src: '', } id = '' @@ -25,9 +24,9 @@ export default class Image extends Component { this.setSrc() } - shouldComponentUpdate(props) { + shouldComponentUpdate(props, state) { // props变更或state.src变更时进行渲染 - return !isEqual(this.props, props) || !this.state.src + return !isEqual(this.props, props) || this.state.src !== state.src } componentDidUpdate() { @@ -46,13 +45,9 @@ export default class Image extends Component { render() { if (this.props.type === 'avatar') { - return ( - - ) + return } else { - return ( - - ) + return } } } diff --git a/web-react/src/components/index.js b/web-react/src/components/index.js index b66489e..6ed3ee0 100644 --- a/web-react/src/components/index.js +++ b/web-react/src/components/index.js @@ -1,4 +1,4 @@ -export { default as AntIcon } from 'components/ant-icon' +export { default as AntIcon } from './ant-icon' export { default as AuthorityView } from './authority-view' export { default as Auth } from './authorized' export { default as ColorSelector } from './form/color-selector' diff --git a/web-react/src/pages/system/account/base.jsx b/web-react/src/pages/system/account/base.jsx new file mode 100644 index 0000000..7d7348b --- /dev/null +++ b/web-react/src/pages/system/account/base.jsx @@ -0,0 +1,186 @@ +import React, { Component } from 'react' +import { Button, Card, Col, Descriptions, Modal, Row, Spin, Tooltip, Upload } from 'antd' +import { AntIcon, Image } from 'components' +import { Cropper } from 'react-cropper' +import 'cropperjs/dist/cropper.css' +import { BlobToFile } from 'util/file' + +import './base.less' +import { api } from 'common/api' + +export default class base extends Component { + state = { + img: true, + + cropperVisible: false, + loadingAvatar: false, + } + + cropper = React.createRef() + + avatarFile = null + + async onOpenAvatarCropper() { + this.setState({ cropperVisible: true }) + } + + onCloseAvatarCropper() { + this.setState({ cropperVisible: false }, () => { + setTimeout(() => { + const cropper = this.cropper.current && this.cropper.current.cropper + if (cropper) { + cropper.destroy() + } + this.avatarFile = null + this.setState({ img: true }) + }, 300) + }) + } + + onUploadAvatar() { + this.setState({ loadingAvatar: true }) + const canvas = this.cropper.current.cropper.getCroppedCanvas() + canvas.toBlob(async data => { + try { + const file = BlobToFile(data, this.avatarFile.name, this.avatarFile.type) + const fd = new FormData() + fd.append('file', file) + const { data: avatar } = await api.sysFileInfoUpload(fd) + await api.sysUserUpdateInfo({ avatar }) + this.onCloseAvatarCropper() + this.props.loadData() + } finally { + this.setState({ loadingAvatar: false }) + } + }) + } + + render() { + const { user } = this.props + + const { img, cropperVisible, loadingAvatar } = this.state + + const cropper = this.cropper.current && this.cropper.current.cropper + + return ( + <> + +
+ +
this.onOpenAvatarCropper()} + className="yo-avatar-info--cover" + > + +
+
+
+ + {user.name} + {user.nickName} + {user.account} + {user.sex} + + {user.birthday && + (typeof user.birthday === 'string' + ? user.birthday + : user.birthday.format('YYYY-MM-DD'))} + + +
+ this.onCloseAvatarCropper()} + > + }> + + +
+ +
+ + +
+ + + { + this.avatarFile = file + const reader = new FileReader() + reader.readAsDataURL(file) + reader.onload = () => { + this.setState({ img: reader.result }) + } + return false + }} + showUploadList={false} + className="mr-xs" + > + + + + + + + + + + + + + ) + } +} diff --git a/web-react/src/pages/system/account/base.less b/web-react/src/pages/system/account/base.less new file mode 100644 index 0000000..1ee4118 --- /dev/null +++ b/web-react/src/pages/system/account/base.less @@ -0,0 +1,51 @@ +@import (reference) '~assets/style/app.less'; +.yo-avatar-info { + position: relative; + + overflow: hidden; + + width: 128px; + margin: 0 auto; + + border-radius: 50%; + &--cover { + font-size: @font-size-lg * 2; + + position: absolute; + top: 0; + left: 0; + + display: flex; + align-items: center; + justify-content: center; + + width: 100%; + height: 100%; + + cursor: pointer; + transition: @animation-duration-slow; + + opacity: 0; + color: @white; + background-color: fade(@black, 50%); + &:hover { + opacity: 1; + } + } +} +.yo-avatar-cropper { + overflow: hidden; + + border-radius: @border-radius-base; + background-color: #ccc; +} +.yo-avatar-preview { + overflow: hidden; + + width: 200px; + height: 200px; + margin: 0 auto; + + border-radius: 50%; + background: #ccc; +} diff --git a/web-react/src/pages/system/account/index.jsx b/web-react/src/pages/system/account/index.jsx index 4492f45..4ba41f6 100644 --- a/web-react/src/pages/system/account/index.jsx +++ b/web-react/src/pages/system/account/index.jsx @@ -1,41 +1,113 @@ 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 ReactDOM from 'react-dom' +import { Anchor, Card, Col, Row, Spin } from 'antd' +import { AntIcon, Container } from 'components' +import moment from 'moment' +import store from 'store' +import { api } from 'common/api' + +import Base from './base' import Info from './setting/info' -import nav from 'store/reducer/nav' +import Safety from './setting/satety' + +const { getState, dispatch, subscribe } = store + +const navs = [ + { title: '基本信息', component: Info }, + { title: '安全设置', component: Safety }, +] export default class index extends Component { - state = {} + state = { + loading: false, + user: getState('user'), + } + + container = window + + constructor(props) { + super(props) + + this.unsubscribe = subscribe('user', user => { + this.setState({ user }) + }) + } + + componentDidMount() { + this.loadData() + } + + componentWillUnmount() { + this.unsubscribe() + } + + loadData = async () => { + this.setState({ loading: true }) + try { + const { data } = await api.getLoginUser() + if (data.birthday) { + data.birthday = moment(data.birthday) + } + dispatch({ + type: 'SET_USER_ACCOUNT', + user: data, + }) + } finally { + this.setState({ loading: false }) + } + } + + setContainer = container => { + this.container = (ReactDOM.findDOMNode(container) || {}).parentNode + } render() { - // let navs = [ - // { - // title: '我的信息', - // component: require('./setting/info'), - // }, - // { - // title: '安全设置', - // component: require('./setting/satety'), - // }, - // ] + const { loadData } = this + + const { loading, user } = this.state - // return ( - // - // - // {navs.map(item => { - // return - // })} - // - //
- //
- // ) return ( -
- - -
+ } ref={this.setContainer}> + + + + this.container} + offsetTop={24} + targetOffset={100} + wrapperStyle={{ backgroundColor: 'transparent' }} + onClick={e => e.preventDefault()} + > + {navs.map((item, i) => ( + + ))} + + + + + +
+ + + + {navs.map((item, i) => ( +
+
+ + + +
+ ))} + +
+ +
+
+
) } } diff --git a/web-react/src/pages/system/account/setting/info.jsx b/web-react/src/pages/system/account/setting/info.jsx index 211d2e1..8af5300 100644 --- a/web-react/src/pages/system/account/setting/info.jsx +++ b/web-react/src/pages/system/account/setting/info.jsx @@ -1,127 +1,87 @@ import React, { Component } from 'react' -import { Button, DatePicker, Form, Input, message, Radio, Spin } from 'antd' +import { Button, DatePicker, Form, Input, message as Message, Radio } from 'antd' +import { AntIcon } from 'components' 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, - }) - }) + const { user } = this.props + if (user.birthday) { + user.birthday = moment(user.birthday) + } + this.form.current.setFieldsValue(user) } - 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, - }) - }) + + async onSvaeInfo() { + this.setState({ saving: true }) + try { + await api.sysUserUpdateInfo(this.form.current.getFieldsValue()) + await this.props.loadData() + Message.success('更新个人信息成功') + } finally { + this.setState({ saving: false }) + } } onAvatarStart() {} render() { - const { info } = this.state + const { user } = this.props + + const { saving } = this.state + return ( - - }> -
-

我的信息

-
- } - type="avatar" - /> -
-
-
- - - - - {info.name} - - - - - - - - - 保密 - - - - - - - - - - - - - - -
-
-
- -
+ ) } } diff --git a/web-react/src/pages/system/account/setting/satety/index.jsx b/web-react/src/pages/system/account/setting/satety/index.jsx index df20c83..e6ab368 100644 --- a/web-react/src/pages/system/account/setting/satety/index.jsx +++ b/web-react/src/pages/system/account/setting/satety/index.jsx @@ -1,179 +1,126 @@ import React, { Component } from 'react' -import { Button, DatePicker, Form, Input, List, message as Message, Spin } from 'antd' +import { List } 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 { AntIcon, ModalForm, QueryTableActions } from 'components' import PasswordForm from './password' + import Mail from './mail' import Phone from './phone' -const { getState } = store + const apiAction = { - update: api.sysUserUpdatePwd, + updatePwd: 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() - } + updatePwdForm = React.createRef() + mailForm = React.createRef() + mhoneForm = React.createRef() + + onOpen(modal) { + modal.current.open() } - /** - * 打开新增/编辑弹窗 - * @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() { + const { user, loadData } = this.props + + const index = [] + //密码 + index.push({ + title: '登录密码', + description: + '安全性高的密码可以使帐号更安全。建议您定期更换密码,设置一个包含字母,符号或数字中至少两项且长度超过6位的密码。', + // extra: ( + //
+ // 当前密码强度为: + // { + // [ + // , + // , + // , + // ][user.securityLevel - 1] + // } + //
+ // ), + done: true, + action: () => { + this.onOpen(this.updatePwdForm) + }, + }) + //手机 + index.push({ + title: '手机绑定', + description: ( +
+ 手机号可以直接用于登录、找回密码等。 + {user.phone && ( + <> + 您已绑定了手机{user.phone} + + )} +
+ ), + done: !!user.phone, + action: () => { + this.onOpen(this.mhoneForm) + }, + }) + //邮箱 + index.push({ + title: '邮箱绑定', + description: ( +
+ 安全邮箱可以直接用于登录、找回密码等。 + {user.email && ( + <> + 您已绑定了邮箱{user.email} + + )} +
+ ), + done: !!user.email, + action: () => { + this.onOpen(this.mailForm) + }, + }) + 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 index e391cc0..4a8467a 100644 --- a/web-react/src/pages/system/account/setting/satety/mail.jsx +++ b/web-react/src/pages/system/account/setting/satety/mail.jsx @@ -1,454 +1,376 @@ 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 { Form, Input, Modal, Spin, Steps, Button, Row, Col, message as Message, Select } from 'antd' +import { AntIcon } from 'components' 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' +import store from 'store' +import { cloneDeep } from 'lodash' -const initialValues = { - orgcode: '', - target: '', - code: '', - type: null, +const { getState } = store + +const steps = [ + { + title: '验证', + }, + { + title: '绑定', + }, +] + +const reg = /^([a-zA-Z]|[0-9])(\w|)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/ + +const initState = { + visible: false, + loading: false, + + // 可用于验证的类型 + types: [], + // 当前步骤 + currentStep: 0, + + nextDisabled: true, + // 正在发送验证码 + sendingCode: false, + // 冷却时间 + countDown: 0, } -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: [], - } + state = cloneDeep(initState) + 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, + + orgCode = '' + + //打开窗口 + open = () => { + this.setState({ visible: true }) + + this.showCountDown() + + const data = getState('user') + const types = [] + data.phone && + types.push({ + title: `使用手机号${data.phone}进行验证`, + value: 1, }) - 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, - }) + data.email && + types.push({ + title: `使用邮箱${data.email}进行验证`, + value: 2, }) + + this.setState({ types }) } - //进入下一步 - 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, - }) - }) + + close() { + this.setState(cloneDeep(initState)) } + /** * 将倒计时添加入到本地 */ addTime() { - const now = Date.now() - var date = now + 60 * 1000 + 500 - window.localStorage.setItem(COUNT_DWON_KEY, date) + const now = Date.now() + 60 * 1000 + window.localStorage.setItem(COUNT_DWON_KEY, now) } /** * 显示倒计时 */ - showcountdown() { - let _this = this - var Furdate = window.localStorage.getItem(COUNT_DWON_KEY) - var nowdate = new Date().getTime() - if (Furdate >= nowdate) { + showCountDown() { + const surplusTime = window.localStorage.getItem(COUNT_DWON_KEY) + const nowTime = Date.now() + if (surplusTime >= nowTime) { this.setState({ - sendOrNo: false, - countdown: parseInt((Furdate - nowdate) / 1000), + countDown: parseInt((surplusTime - nowTime) / 1000), }) setTimeout(() => { - _this.showcountdown() + this.showCountDown() }, 1000) } else { - this.setState({ - sendOrNo: true, - }) + this.setState({ countDown: 0 }) } } - //打开窗口 - 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, - }) + + //发送验证码 + async onSendCode() { + const form = this.form.current + + const valid = await form.validateFields() + if (!valid) { + return + } + + this.setState({ sendingCode: true }) + + const data = form.getFieldsValue() + try { + await api.sysUserSendCode(data) + const typeName = data.type ? [, '手机', '邮箱'][data.type] : '手机' + Message.success(`已发送验证码到${typeName},请注意查收`) + this.addTime() + this.showCountDown() + } finally { + this.setState({ sendingCode: false }) + } + } + + // 下一步 + async onNext() { + this.setState({ loading: true }) + const data = this.form.current.getFieldsValue() + this.orgCode = data.orgCode + + try { + await api.sysUserCheckBindcode(data) + window.localStorage.removeItem(COUNT_DWON_KEY) this.setState({ - type: index, + nextDisabled: true, + currentStep: this.state.currentStep + 1, }) - if (index.length > 0) { - this.form.current.setFieldsValue({ - type: index[0].Value, - }) - } + } finally { 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, - }) } + } + + // 上一步 + onPrev() { + window.localStorage.removeItem(COUNT_DWON_KEY) + this.setState({ + currentStep: this.state.currentStep - 1, + }) + } + + //完成 + async onComplete() { + this.setState({ loading: true }) + try { + await api.sysUserCheckBindcode({ + ...this.form.current.getFieldsValue(), + orgCode: this.orgCode, + }) + await this.props.loadData() + window.localStorage.removeItem(COUNT_DWON_KEY) + Message.success('绑定邮箱成功') + this.close() + } finally { + this.setState({ loading: false }) + } + } + + renderForm() { + const { nextDisabled, sendingCode, countDown } = this.state + return ( - - +
{ + this.setState({ + nextDisabled: !(allValues.target && allValues.code), + }) + }} > - }> -
- {this.state.type.length !== 0 ? ( -
-
- - - - - {steps.map(item => ( - - ))} - - - - -
- { - this.setState({ - buttondisabled: !( - allValues.orgcode || - (allValues.target && allValues.code) - ), - }) - }} - > -
- {this.state.current == 0 && ( -
- - - - - - - - - - {this.state.sendOrNo ? ( - - ) : ( - - )} - - - -
- )} - {this.state.current == 1 && ( -
- - - - - - - - - - {this.state.sendOrNo ? ( - - ) : ( - - )} - - - -
- )} -
- -
-
- {this.state.current == 0 && ( - <> -
- -
-
- - )} - {this.state.current == 1 && ( - <> - {this.state.current > 0 && ( - - )} - - - )} -
-
- ) : ( -
-
{ - this.setState({ - buttondisabled: !( - allValues.target && allValues.code - ), - }) - }} - > - - + + + + + + + + + + + + {countDown ? ( + + ) : ( + + )} + + + + +
+ +
+
+ ) + } + + renderStepForm() { + const { types, currentStep, sendingCode, countDown } = this.state + + return ( +
+ + + + {steps.map(item => ( + + ))} + + + +
{ + this.setState({ + nextDisabled: !( + allValues.orgCode || + (allValues.target && allValues.code) + ), + }) + }} + > + {currentStep === 0 && ( + <> + + + + + + + + - - - - - - - {this.state.sendOrNo ? ( - - ) : ( - - )} - - - - -
- - - - - - - -
-
- )} -
-
-
-
+ ) : ( + + )} + + + + + )} + {currentStep === 1 && ( + <> + + + + + + + + + + + + {countDown ? ( + + ) : ( + + )} + + + + + )} + +
+ {currentStep === 0 && ( + <> + + + )} + {currentStep === 1 && ( + <> + + + + )} +
+
+ ) + } + + render() { + const { visible, loading, types } = this.state + + return ( + this.close()} + visible={visible} + className="yo-modal-form" + title="绑定邮箱" + > + }> +
+ {types.length ? this.renderStepForm() : this.renderForm()} +
+
+
) } } diff --git a/web-react/src/pages/system/account/setting/satety/password.jsx b/web-react/src/pages/system/account/setting/satety/password.jsx index 0616fc1..6e0c9db 100644 --- a/web-react/src/pages/system/account/setting/satety/password.jsx +++ b/web-react/src/pages/system/account/setting/satety/password.jsx @@ -1,15 +1,9 @@ import React, { Component } from 'react' -import { Form, Input, InputNumber, Spin } from 'antd' -import { AntIcon, IconSelector } from 'components' -import { cloneDeep } from 'lodash' +import { Form, Input } from 'antd' -const initialValues = { - sort: 100, -} export default class form extends Component { state = { // 加载状态 - loading: true, exist: false, } // 表单实例 @@ -31,25 +25,7 @@ export default class form extends Component { * [异步,必要] * @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, - }) - } + async fillData() {} /** * 获取数据 @@ -63,9 +39,6 @@ export default class form extends Component { const valid = await form.validateFields() if (valid) { const postData = form.getFieldsValue() - if (this.record) { - postData.id = this.record.id - } //#region 从前段转换后端所需格式 //#endregion return postData @@ -75,32 +48,39 @@ export default class form extends Component { render() { return (
- - {/* */} -
- - - - - - - - - -
-
+
+ + + + + + + ({ + validator(_, value) { + if (!value || getFieldValue('newPassword') === value) { + return Promise.resolve() + } + return Promise.reject(new Error('确认新密码不匹配')) + }, + }), + ]} + name="confirm" + > + + +
) } diff --git a/web-react/src/pages/system/account/setting/satety/phone.jsx b/web-react/src/pages/system/account/setting/satety/phone.jsx index d5ebb56..f0bb4e9 100644 --- a/web-react/src/pages/system/account/setting/satety/phone.jsx +++ b/web-react/src/pages/system/account/setting/satety/phone.jsx @@ -1,455 +1,376 @@ 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 { Form, Input, Modal, Spin, Steps, Button, Row, Col, message as Message, Select } from 'antd' +import { AntIcon } from 'components' 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' +import store from 'store' +import { cloneDeep } from 'lodash' -const initialValues = { - orgcode: '', - target: '', - code: '', - type: null, +const { getState } = store + +const steps = [ + { + title: '验证', + }, + { + title: '绑定', + }, +] + +const 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}$/ + +const initState = { + visible: false, + loading: false, + + // 可用于验证的类型 + types: [], + // 当前步骤 + currentStep: 0, + + nextDisabled: true, + // 正在发送验证码 + sendingCode: false, + // 冷却时间 + countDown: 0, } -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: [], - } + state = cloneDeep(initState) + 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, + + orgCode = '' + + //打开窗口 + open = () => { + this.setState({ visible: true }) + + this.showCountDown() + + const data = getState('user') + const types = [] + data.phone && + types.push({ + title: `使用手机号${data.phone}进行验证`, + value: 1, }) - 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, - }) + data.email && + types.push({ + title: `使用邮箱${data.email}进行验证`, + value: 2, }) + + this.setState({ types }) } - //进入下一步 - 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, - }) - }) + + close() { + this.setState(cloneDeep(initState)) } + /** * 将倒计时添加入到本地 */ addTime() { - const now = Date.now() - var date = now + 60 * 1000 + 500 - window.localStorage.setItem(COUNT_DWON_KEY, date) + const now = Date.now() + 60 * 1000 + window.localStorage.setItem(COUNT_DWON_KEY, now) } /** * 显示倒计时 */ - showcountdown() { - let _this = this - var Furdate = window.localStorage.getItem(COUNT_DWON_KEY) - var nowdate = new Date().getTime() - if (Furdate >= nowdate) { + showCountDown() { + const surplusTime = window.localStorage.getItem(COUNT_DWON_KEY) + const nowTime = Date.now() + if (surplusTime >= nowTime) { this.setState({ - sendOrNo: false, - countdown: parseInt((Furdate - nowdate) / 1000), + countDown: parseInt((surplusTime - nowTime) / 1000), }) setTimeout(() => { - _this.showcountdown() + this.showCountDown() }, 1000) } else { - this.setState({ - sendOrNo: true, - }) + this.setState({ countDown: 0 }) } } - //打开窗口 - 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, - }) + + //发送验证码 + async onSendCode() { + const form = this.form.current + + const valid = await form.validateFields() + if (!valid) { + return + } + + this.setState({ sendingCode: true }) + + const data = form.getFieldsValue() + try { + await api.sysUserSendCode(data) + const typeName = data.type ? [, '手机', '邮箱'][data.type] : '手机' + Message.success(`已发送验证码到${typeName},请注意查收`) + this.addTime() + this.showCountDown() + } finally { + this.setState({ sendingCode: false }) + } + } + + // 下一步 + async onNext() { + this.setState({ loading: true }) + const data = this.form.current.getFieldsValue() + this.orgCode = data.orgCode + + try { + await api.sysUserCheckBindcode(data) + window.localStorage.removeItem(COUNT_DWON_KEY) this.setState({ - type: index, + nextDisabled: true, + currentStep: this.state.currentStep + 1, }) - if (index.length > 0) { - this.form.current.setFieldsValue({ - type: index[0].Value, - }) - } + } finally { 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, - }) } + } + + // 上一步 + onPrev() { + window.localStorage.removeItem(COUNT_DWON_KEY) + this.setState({ + currentStep: this.state.currentStep - 1, + }) + } + + //完成 + async onComplete() { + this.setState({ loading: true }) + try { + await api.sysUserCheckBindcode({ + ...this.form.current.getFieldsValue(), + orgCode: this.orgCode, + }) + await this.props.loadData() + window.localStorage.removeItem(COUNT_DWON_KEY) + Message.success('绑定手机号成功') + this.close() + } finally { + this.setState({ loading: false }) + } + } + + renderForm() { + const { nextDisabled, sendingCode, countDown } = this.state + return ( - - +
{ + this.setState({ + nextDisabled: !(allValues.target && allValues.code), + }) + }} > - }> -
- {this.state.type.length !== 0 ? ( -
-
- - - - - {steps.map(item => ( - - ))} - - - - -
- { - this.setState({ - buttondisabled: !( - allValues.orgcode || - (allValues.target && allValues.code) - ), - }) - }} - > -
- {this.state.current == 0 && ( -
- - - - - - - - - - {this.state.sendOrNo ? ( - - ) : ( - - )} - - - -
- )} - {this.state.current == 1 && ( -
- - - - - - - - - - {this.state.sendOrNo ? ( - - ) : ( - - )} - - - -
- )} -
- -
-
- {this.state.current == 0 && ( - <> -
- -
-
- - )} - {this.state.current == 1 && ( - <> - {this.state.current > 0 && ( - - )} - - - )} -
-
- ) : ( -
-
{ - this.setState({ - buttondisabled: !( - allValues.target && allValues.code - ), - }) - }} - > - - + + + + + + + + + + + + {countDown ? ( + + ) : ( + + )} + + + + +
+ +
+
+ ) + } + + renderStepForm() { + const { types, currentStep, sendingCode, countDown } = this.state + + return ( +
+ + + + {steps.map(item => ( + + ))} + + + +
{ + this.setState({ + nextDisabled: !( + allValues.orgCode || + (allValues.target && allValues.code) + ), + }) + }} + > + {currentStep === 0 && ( + <> + + + + + + + + - - - - - - - {this.state.sendOrNo ? ( - - ) : ( - - )} - - - - -
- - - - - - - -
-
- )} -
-
-
-
+ ) : ( + + )} + +
+ + + )} + {currentStep === 1 && ( + <> + + + + + + + + + + + + {countDown ? ( + + ) : ( + + )} + + + + + )} + +
+ {currentStep === 0 && ( + <> + + + )} + {currentStep === 1 && ( + <> + + + + )} +
+ + ) + } + + render() { + const { visible, loading, types } = this.state + + return ( + this.close()} + visible={visible} + className="yo-modal-form" + title="绑定手机" + > + }> +
+ {types.length ? this.renderStepForm() : this.renderForm()} +
+
+
) } } diff --git a/web-react/src/views/main/_layout/header/user.jsx b/web-react/src/views/main/_layout/header/user.jsx index 083aafa..d637fb6 100644 --- a/web-react/src/views/main/_layout/header/user.jsx +++ b/web-react/src/views/main/_layout/header/user.jsx @@ -94,9 +94,10 @@ class User extends Component { 个人中心 - 其他菜单 - 其他菜单 - 其他菜单 + + + 操作手册 + this.onLogout()}> diff --git a/web-react/yarn.lock b/web-react/yarn.lock index 1bb675f..62e133f 100644 --- a/web-react/yarn.lock +++ b/web-react/yarn.lock @@ -3762,6 +3762,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cropperjs@^1.5.12: + version "1.5.12" + resolved "https://registry.nlark.com/cropperjs/download/cropperjs-1.5.12.tgz?cache=0&sync_timestamp=1623485718941&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcropperjs%2Fdownload%2Fcropperjs-1.5.12.tgz#d9c0db2bfb8c0d769d51739e8f916bbc44e10f50" + integrity sha1-2cDbK/uMDXadUXOej5FrvEThD1A= + cross-fetch@^3.0.4: version "3.1.4" resolved "https://registry.npm.taobao.org/cross-fetch/download/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" @@ -9686,6 +9691,13 @@ react-color@^2.19.3: reactcss "^1.2.0" tinycolor2 "^1.4.1" +react-cropper@^2.1.8: + version "2.1.8" + resolved "https://registry.nlark.com/react-cropper/download/react-cropper-2.1.8.tgz?cache=0&sync_timestamp=1624865139834&other_urls=https%3A%2F%2Fregistry.nlark.com%2Freact-cropper%2Fdownload%2Freact-cropper-2.1.8.tgz#bf35a7de65769f8ad357e8ae884e791fe3fe9212" + integrity sha1-vzWn3mV2n4rTV+iuiE55H+P+khI= + dependencies: + cropperjs "^1.5.12" + react-dev-utils@^11.0.3: version "11.0.4" resolved "https://registry.npm.taobao.org/react-dev-utils/download/react-dev-utils-11.0.4.tgz?cache=0&sync_timestamp=1615231838520&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dev-utils%2Fdownload%2Freact-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a"