diff --git a/eiam-console/src/main/console-fe/src/app.tsx b/eiam-console/src/main/console-fe/src/app.tsx index f6770a65..5540ca23 100644 --- a/eiam-console/src/main/console-fe/src/app.tsx +++ b/eiam-console/src/main/console-fe/src/app.tsx @@ -53,9 +53,9 @@ const goLogin = () => { * 获取当前用户信息 */ const fetchUserInfo = async (): Promise => { - const result = await getCurrent().catch(() => undefined); - if (result?.success && result.result) { - return result.result; + const { result, success } = await getCurrent(); + if (success && result) { + return result; } return undefined; }; diff --git a/eiam-console/src/main/console-fe/src/pages/user/Profile/components/Base.tsx b/eiam-console/src/main/console-fe/src/pages/user/Profile/components/Base.tsx index f585974b..658c1b0f 100644 --- a/eiam-console/src/main/console-fe/src/pages/user/Profile/components/Base.tsx +++ b/eiam-console/src/main/console-fe/src/pages/user/Profile/components/Base.tsx @@ -16,17 +16,11 @@ * along with this program. If not, see . */ import { UploadOutlined } from '@ant-design/icons'; -import { - ProForm, - ProFormText, - useStyle as useAntdStyle, -} from '@ant-design/pro-components'; +import { ProForm, ProFormText, useStyle as useAntdStyle } from '@ant-design/pro-components'; import { App, Avatar, Button, Form, Skeleton, Upload } from 'antd'; import { useState } from 'react'; import { changeBaseInfo } from '../service'; -import { aesEcbEncrypt } from '@/utils/aes'; -import { onGetEncryptSecret } from '@/utils/utils'; import { useAsyncEffect } from 'ahooks'; import ImgCrop from 'antd-img-crop'; import { uploadFile } from '@/services/upload'; @@ -114,7 +108,7 @@ const BaseView = () => { const useApp = App.useApp(); const { wrapSSR, hashId } = useStyle(); const [loading, setLoading] = useState(); - const { initialState } = useModel('@@initialState'); + const { initialState, setInitialState } = useModel('@@initialState'); const [avatarURL, setAvatarURL] = useState(initialState?.currentUser?.avatar); const [name, setName] = useState(''); @@ -123,28 +117,22 @@ const BaseView = () => { if (initialState && initialState.currentUser) { setAvatarURL(initialState?.currentUser?.avatar); setName(initialState?.currentUser?.fullName || initialState?.currentUser?.username); - setLoading(false); + setTimeout(async () => { + setLoading(false); + }, 500); } }, [initialState]); const handleFinish = async (values: Record) => { - //加密传输 - const publicSecret = await onGetEncryptSecret(); - if (publicSecret) { - const { success } = await changeBaseInfo( - aesEcbEncrypt( - JSON.stringify({ - fullName: values.fullName, - nickName: values.nickName, - personalProfile: values.personalProfile, - avatar: avatarURL, - }), - publicSecret, - ), - ); - if (success) { - useApp.message.success(intl.formatMessage({ id: 'app.update_success' })); - } + const { success } = await changeBaseInfo({ + fullName: values.fullName, + nikeName: values.nikeName, + }); + if (success) { + useApp.message.success(intl.formatMessage({ id: 'app.update_success' })); + //获取当前用户信息 + const currentUser = await initialState?.fetchUserInfo?.(); + await setInitialState((s: any) => ({ ...s, currentUser: currentUser })); } }; @@ -218,7 +206,7 @@ const BaseView = () => { return wrapSSR(
{loading ? ( - + ) : ( <>
@@ -232,7 +220,9 @@ const BaseView = () => { return {dom}; }, searchConfig: { - submitText: intl.formatMessage({ id: 'app.save' }), + submitText: intl.formatMessage({ + id: 'page.user.profile.base.form.update_button', + }), }, resetButtonProps: { style: { @@ -246,6 +236,12 @@ const BaseView = () => { }} requiredMark={false} > + @@ -110,49 +110,44 @@ export default (props: { rules={[ { required: true, - message: intl.formatMessage({ id: 'page.user.profile.modify_email.form.email.rule.0' }), + message: intl.formatMessage({ + id: 'page.user.profile.modify_email.form.email.rule.0', + }), }, { type: 'email', - message: intl.formatMessage({ id: 'page.user.profile.modify_email.form.email.rule.1' }), + message: intl.formatMessage({ + id: 'page.user.profile.modify_email.form.email.rule.1', + }), }, ]} onGetCaptcha={async (email) => { if (!(await formRef.current?.validateFields([FieldNames.PASSWORD]))) { return Promise.reject(); } - const publicSecret = await onGetEncryptSecret(); - if (publicSecret !== undefined) { - //加密传输 - const { success, message, result, status } = await prepareChangeEmail( - aesEcbEncrypt( - JSON.stringify({ - email: email, - password: formRef.current?.getFieldValue(FieldNames.PASSWORD), - }), - publicSecret, - ), - ); - if (!success && status === ServerExceptionStatus.PASSWORD_VALIDATED_FAIL_ERROR) { - formRef.current?.setFields([ - { name: FieldNames.PASSWORD, errors: [`${message}`] }, - ]); - return Promise.reject(); - } - if (success && result) { - setHasSendCaptcha(true); - useApp.message.success(intl.formatMessage({ id: 'app.send_successfully' })); - return Promise.resolve(); - } - useApp.message.error(message); - captchaRef.current?.endTiming(); + const { success, message, result, status } = await prepareChangeEmail({ + email: email, + password: formRef.current?.getFieldValue(FieldNames.PASSWORD), + }); + if (!success && status === ServerExceptionStatus.PASSWORD_VALIDATED_FAIL_ERROR) { + formRef.current?.setFields([{ name: FieldNames.PASSWORD, errors: [`${message}`] }]); return Promise.reject(); } + if (success && result) { + setHasSendCaptcha(true); + useApp.message.success(intl.formatMessage({ id: 'app.send_successfully' })); + return Promise.resolve(); + } + useApp.message.error(message); + captchaRef.current?.endTiming(); + return Promise.reject(); }} /> . */ -import {FieldNames} from '../constant'; -import {changePhone} from '../service'; -import type {CaptFieldRef, ProFormInstance} from '@ant-design/pro-components'; -import {ModalForm, ProFormText, useStyle as useAntdStyle,} from '@ant-design/pro-components'; -import {App, ConfigProvider, Spin} from 'antd'; -import {omit} from 'lodash'; -import {useContext, useEffect, useRef, useState} from 'react'; -import {FormLayout} from './constant'; +import { changePhone, prepareChangePhone } from '../service'; +import { phoneIsValidNumber } from '@/utils/utils'; +import { FormattedMessage } from '@@/plugin-locale/localeExports'; +import type { CaptFieldRef, ProFormInstance } from '@ant-design/pro-components'; +import { + ModalForm, + ProFormCaptcha, + ProFormDependency, + ProFormText, + useStyle as useAntdStyle, +} from '@ant-design/pro-components'; +import { App, ConfigProvider, Spin } from 'antd'; +import { omit } from 'lodash'; +import { useContext, useEffect, useRef, useState } from 'react'; +import { FormLayout } from './constant'; import classnames from 'classnames'; -import {ConfigContext} from 'antd/es/config-provider'; -import {useIntl} from '@@/exports'; +import { ConfigContext } from 'antd/es/config-provider'; +import { useIntl } from '@@/exports'; +import FormPhoneAreaCodeSelect from '@/components/FormPhoneAreaCodeSelect'; +import { FieldNames, ServerExceptionStatus } from '../constant'; +import * as React from 'react'; function useStyle(prefixCls: string) { const { getPrefixCls } = useContext(ConfigContext || ConfigProvider.ConfigContext); @@ -57,8 +67,6 @@ export default (props: { const captchaRef = useRef(); /**已发送验证码*/ const [hasSendCaptcha, setHasSendCaptcha] = useState(false); - /**手机区域*/ - const [phoneRegion, setPhoneRegion] = useState('86'); const formRef = useRef(); const { wrapSSR, hashId } = useStyle(prefixCls); @@ -89,7 +97,9 @@ export default (props: { }} onFinish={async (formData: Record) => { if (!hasSendCaptcha) { - useApp.message.error(intl.formatMessage({ id: 'page.user.profile.please_send_code.message' })); + useApp.message.error( + intl.formatMessage({ id: 'page.user.profile.please_send_code.message' }), + ); return Promise.reject(); } const { success } = await changePhone(omit(formData, FieldNames.PASSWORD)); @@ -118,7 +128,84 @@ export default (props: { }, ]} /> - + + {({ phoneAreaCode }) => { + return ( + , + }, + { + validator: async (rule, value) => { + if (!value) { + return Promise.resolve(); + } + //校验手机号格式 + const isValidNumber = await phoneIsValidNumber(value, phoneAreaCode); + if (!isValidNumber) { + return Promise.reject( + new Error( + intl.formatMessage({ + id: 'page.user.profile.common.form.phone.rule.1', + }), + ), + ); + } + }, + validateTrigger: ['onBlur'], + }, + ]} + phoneName={FieldNames.PHONE} + addonBefore={ + + } + onGetCaptcha={async (mobile) => { + if (!(await formRef.current?.validateFields([FieldNames.PASSWORD]))) { + return Promise.reject(); + } + const { success, message, status, result } = await prepareChangePhone({ + phone: mobile as string, + phoneRegion: phoneAreaCode, + password: formRef.current?.getFieldValue(FieldNames.PASSWORD), + }); + if (!success && status === ServerExceptionStatus.PASSWORD_VALIDATED_FAIL_ERROR) { + formRef.current?.setFields([ + { name: FieldNames.PASSWORD, errors: [`${message}`] }, + ]); + return Promise.reject(); + } + if (success && result) { + setHasSendCaptcha(true); + useApp.message.success(intl.formatMessage({ id: 'app.send_successfully' })); + return Promise.resolve(); + } + useApp.message.error(message); + captchaRef.current?.endTiming(); + return Promise.reject(); + }} + /> + ); + }} + . */ export default { - 'page.user.profile.menu.base': '基本设置', + 'page.user.profile.menu.base': '基础信息', 'page.user.profile.menu.security': '安全设置', 'page.user.profile.menu.bind': '账号绑定', 'page.user.profile.base.avatar_title': '头像', 'page.user.profile.base.avatar_change_title': '更换头像', - 'page.user.profile.base.form.username': '用户名称', + 'page.user.profile.base.form.account_id': '账户ID', + 'page.user.profile.base.form.username': '用户名', 'page.user.profile.base.form.email': '邮箱', 'page.user.profile.base.form.phone': '手机号', 'page.user.profile.base.form.full_name': '姓名', 'page.user.profile.base.form.nick_name': '昵称', 'page.user.profile.base.form.nick_name.rule.0': '请输入您的昵称', + 'page.user.profile.base.form.update_button': '更新', 'page.user.profile.common.form.password': '密码', 'page.user.profile.common.form.password.placeholder': '请输入密码', 'page.user.profile.common.form.password.rule.0': '请输入密码', @@ -35,7 +37,6 @@ export default { 'page.user.profile.common.form.phone.placeholder': '请输入手机号', 'page.user.profile.common.form.phone.rule.0': '手机号未填写', 'page.user.profile.common.form.phone.rule.1': '手机号不合法', - 'page.user.profile.common.form.phone.rule.2': '手机号已存在', 'page.user.profile.common.form.code': '验证码', 'page.user.profile.common.form.code.placeholder': '请输入验证码', @@ -50,7 +51,8 @@ export default { 'page.user.profile.bind.totp.form.verify.placeholder': '输入密码确认身份', 'page.user.profile.bind.totp.form.bind': '绑定动态口令', 'page.user.profile.bind.totp.form.bind.placeholder': '使用移动端认证器绑定口令', - 'page.user.profile.bind.totp.form.bind.alert': '请使用市面常见认证器 APP,扫描下方二维码,完成绑定。', + 'page.user.profile.bind.totp.form.bind.alert': + '请使用市面常见认证器 APP,扫描下方二维码,完成绑定。', 'page.user.profile.bind.totp.form.bind.paragraph': '扫码绑定后,请您输入移动端 APP 中的六位动态口令,完成本次绑定。', 'page.user.profile.bind.totp.form.update_email': '修改邮箱', diff --git a/eiam-console/src/main/console-fe/src/pages/user/Profile/service.ts b/eiam-console/src/main/console-fe/src/pages/user/Profile/service.ts index 5bc8aedf..8d7bc468 100644 --- a/eiam-console/src/main/console-fe/src/pages/user/Profile/service.ts +++ b/eiam-console/src/main/console-fe/src/pages/user/Profile/service.ts @@ -15,17 +15,20 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { ParamCheckType } from '@/constant'; import { request } from '@@/plugin-request/request'; /** * 准备修改手机号 * - * @param encrypt + * @param data */ -export async function prepareChangePhone(encrypt: string): Promise> { +export async function prepareChangePhone(data: { + phone: string; + phoneRegion: string; + password: string; +}): Promise> { return request(`/api/v1/user/profile/prepare_change_phone`, { - data: { encrypt: encrypt }, + data: data, method: 'POST', skipErrorHandler: true, }).catch(({ response: { data } }) => { @@ -48,11 +51,14 @@ export async function changePhone(data: Record): Promise> { +export async function prepareChangeEmail(data: { + password: string; + email: string; +}): Promise> { return request(`/api/v1/user/profile/prepare_change_email`, { - data: { encrypt: encrypt }, + data: data, method: 'POST', skipErrorHandler: true, }).catch(({ response: { data } }) => { @@ -75,11 +81,14 @@ export async function changeEmail(data: Record): Promise> { +export async function changePassword(data: { + oldPassword: string; + newPassword: string; +}): Promise> { return request(`/api/v1/user/profile/change_password`, { - data: { encrypt: encrypt }, + data: data, method: 'PUT', skipErrorHandler: true, }).catch(({ response: { data } }) => { @@ -90,32 +99,17 @@ export async function changePassword(encrypt: string): Promise> { +export async function changeBaseInfo( + data: Record, +): Promise> { return request(`/api/v1/user/profile/change_info`, { - data: { encrypt: encrypt }, + data: data, method: 'PUT', }); } -/** - * 验证用户信息 - * - * @param type - * @param value - * @param id - */ -export async function userParamCheck( - type: ParamCheckType, - value: string, - id?: string, -): Promise> { - return request(`/api/v1/user/param_check`, { - params: { id, type, value }, - method: 'GET', - }); -} /** * 准备修改账户密码 diff --git a/eiam-console/src/main/console-fe/src/services/typings.d.ts b/eiam-console/src/main/console-fe/src/services/typings.d.ts index e33acf4f..a3a46111 100644 --- a/eiam-console/src/main/console-fe/src/services/typings.d.ts +++ b/eiam-console/src/main/console-fe/src/services/typings.d.ts @@ -67,7 +67,6 @@ declare namespace API { secret: string; }; - /** * 加密秘钥 */ @@ -222,8 +221,8 @@ declare namespace AccountAPI { appName: string; clientIp: string; userAgent: { - platformVersion:string; - platform:string + platformVersion: string; + platform: string; }; browser: string; eventStatus: string; diff --git a/eiam-console/src/main/console-fe/src/services/user.ts b/eiam-console/src/main/console-fe/src/services/user.ts index ca28fa7a..4e6589f9 100644 --- a/eiam-console/src/main/console-fe/src/services/user.ts +++ b/eiam-console/src/main/console-fe/src/services/user.ts @@ -20,6 +20,6 @@ import { request } from '@umijs/max'; /** * 获取当前用户 */ -export async function getCurrent(): Promise | undefined> { +export async function getCurrent(): Promise> { return request>('/api/v1/session/current_user'); }