feat(web): user email support change
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 14:00:23
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 14:00:23
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-25 11:17:44
|
||||
*/
|
||||
import { request } from '@/utils/request'
|
||||
import type { CreateModalData } from '@/views/UserManagement/types'
|
||||
import type { CreateModalData, ChangeEmailModalForm } from '@/views/UserManagement/types'
|
||||
import { cookieUtils } from '@/utils/request'
|
||||
|
||||
// User info
|
||||
@@ -28,6 +28,10 @@ export const refreshToken = () => {
|
||||
export const changePassword = (data: { user_id: string; new_password: string }) => {
|
||||
return request.put('/users/admin/change-password', data)
|
||||
}
|
||||
// Verify password
|
||||
export const verifyPassword = (data: { password: string }) => {
|
||||
return request.post('/users/verify_pwd', data)
|
||||
}
|
||||
// Disable user
|
||||
export const deleteUser = (user_id: string) => {
|
||||
return request.delete(`/users/${user_id}`)
|
||||
@@ -44,4 +48,12 @@ export const addUser = (data: CreateModalData) => {
|
||||
export const logoutUrl = '/logout'
|
||||
export const logout = () => {
|
||||
return request.post(logoutUrl)
|
||||
}
|
||||
// Send email verification code
|
||||
export const sendEmailCode = (data: { email: string }) => {
|
||||
return request.post('/users/send-email-code', data)
|
||||
}
|
||||
// Verify code and change email
|
||||
export const changeEmail = (data: ChangeEmailModalForm) => {
|
||||
return request.put('/users/change-email', data)
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-02 15:03:25
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-02 15:47:31
|
||||
* @Last Modified time: 2026-02-25 11:14:25
|
||||
*/
|
||||
/**
|
||||
* Empty Component
|
||||
@@ -13,7 +13,7 @@
|
||||
* @component
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
import { type FC, type ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import emptyIcon from '@/assets/images/empty/empty.svg';
|
||||
@@ -24,7 +24,7 @@ interface EmptyProps {
|
||||
/** Icon size - single number or [width, height] array */
|
||||
size?: number | number[];
|
||||
/** Main title text */
|
||||
title?: string;
|
||||
title?: string | ReactElement;
|
||||
/** Whether to show subtitle */
|
||||
isNeedSubTitle?: boolean;
|
||||
/** Custom subtitle text */
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-02 15:09:47
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-02 15:51:54
|
||||
* @Last Modified time: 2026-02-25 11:40:47
|
||||
*/
|
||||
/**
|
||||
* UserInfoModal Component
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState, useRef } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import { Button, Space } from 'antd';
|
||||
import { UnlockOutlined } from '@ant-design/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -23,7 +23,9 @@ import { useUser } from '@/store/user';
|
||||
import RbModal from '@/components/RbModal'
|
||||
import { formatDateTime } from '@/utils/format';
|
||||
import ResetPasswordModal from '@/views/UserManagement/components/ResetPasswordModal'
|
||||
import type { ResetPasswordModalRef } from '@/views/UserManagement/types'
|
||||
import type { ResetPasswordModalRef, VerifyPasswordModalRef, ChangeEmailModalRef } from '@/views/UserManagement/types'
|
||||
import VerifyPasswordModal from '@/views/UserManagement/components/VerifyPasswordModal'
|
||||
import ChangeEmailModal from '@/views/UserManagement/components/ChangeEmailModal'
|
||||
|
||||
/** Interface for UserInfoModal ref methods exposed to parent components */
|
||||
export interface UserInfoModalRef {
|
||||
@@ -37,8 +39,10 @@ export interface UserInfoModalRef {
|
||||
const UserInfoModal = forwardRef<UserInfoModalRef>((_props, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const resetPasswordModalRef = useRef<ResetPasswordModalRef>(null)
|
||||
const { user } = useUser();
|
||||
const { user, getUserInfo } = useUser();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const verifyPasswordModalRef = useRef<VerifyPasswordModalRef>(null)
|
||||
const changeEmailModalRef = useRef<ChangeEmailModalRef>(null)
|
||||
|
||||
/** Close the modal */
|
||||
const handleClose = () => {
|
||||
@@ -50,6 +54,17 @@ const UserInfoModal = forwardRef<UserInfoModalRef>((_props, ref) => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
/** Open password verification modal before editing email */
|
||||
const handleEditEmail = () => {
|
||||
verifyPasswordModalRef.current?.handleOpen()
|
||||
}
|
||||
|
||||
/** Update user information after email change */
|
||||
const updateUserInfo = () => {
|
||||
localStorage.removeItem('user')
|
||||
getUserInfo()
|
||||
}
|
||||
|
||||
/** Expose handleOpen and handleClose methods to parent component via ref */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
@@ -74,7 +89,13 @@ const UserInfoModal = forwardRef<UserInfoModalRef>((_props, ref) => {
|
||||
{/* Email */}
|
||||
<div className="rb:flex rb:justify-between rb:text-[#5B6167] rb:text-[14px] rb:leading-5 rb:mb-3">
|
||||
<span className="rb:whitespace-nowrap">{t('user.email')}</span>
|
||||
<span className="rb:text-[#212332]">{user.email}</span>
|
||||
<Space size={8} className="rb:text-[#212332]">
|
||||
{user.email}
|
||||
<div
|
||||
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/editBorder.svg')] rb:hover:bg-[url('@/assets/images/editBg.svg')]"
|
||||
onClick={handleEditEmail}
|
||||
></div>
|
||||
</Space>
|
||||
</div>
|
||||
{/* Role */}
|
||||
<div className="rb:flex rb:justify-between rb:text-[#5B6167] rb:text-[14px] rb:leading-5 rb:mb-3">
|
||||
@@ -106,6 +127,14 @@ const UserInfoModal = forwardRef<UserInfoModalRef>((_props, ref) => {
|
||||
ref={resetPasswordModalRef}
|
||||
source="changePassword"
|
||||
/>
|
||||
<VerifyPasswordModal
|
||||
ref={verifyPasswordModalRef}
|
||||
refresh={() => changeEmailModalRef.current?.handleOpen()}
|
||||
/>
|
||||
<ChangeEmailModal
|
||||
ref={changeEmailModalRef}
|
||||
refresh={updateUserInfo}
|
||||
/>
|
||||
</RbModal>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -274,6 +274,28 @@ export const en = {
|
||||
createdAt: 'Creation Time',
|
||||
member: 'Member',
|
||||
passwordRule: 'password should have at least 6 characters',
|
||||
authVerify: 'Identity Verification',
|
||||
authVerifyDesc: 'For security reasons, please verify your login password first',
|
||||
verify: 'Verify',
|
||||
loginPassword: 'Login Password',
|
||||
loginPasswordPlaceholder: 'Please enter the login password for the current account',
|
||||
loginPasswordVerifyFailed: 'Incorrect password, please try again',
|
||||
bindNewEmail: 'Bind New Email',
|
||||
sureChange: 'Confirm Change',
|
||||
sendEmailCode: 'Send Verification Code',
|
||||
currentEmail: 'Current Email',
|
||||
newEmail: 'New Email Address',
|
||||
emailCode: 'Verification Code',
|
||||
emailCodePlaceholder: 'Please enter the verification code received by the new email',
|
||||
sureChangeEmail: 'Confirm to change the bound email to',
|
||||
sureChangeEmailDesc: '?',
|
||||
changeSuccess: 'Changed successfully',
|
||||
sendSuccess: 'Verification code has been sent, please check',
|
||||
newEmailSameAsOld: 'New email cannot be the same as current email',
|
||||
emailCodeLengthRule: 'Please enter a 6-digit verification code',
|
||||
emailFormatError: 'Incorrect email format',
|
||||
sendCodeTooFrequent: 'Please resend after {{seconds}}s',
|
||||
retrySend: 'Can resend after {{seconds}}s',
|
||||
},
|
||||
timezones: {
|
||||
'Asia/Shanghai': 'China Standard Time (UTC+8)',
|
||||
|
||||
@@ -946,18 +946,29 @@ export const zh = {
|
||||
email: '邮箱',
|
||||
createdAt: '创建时间',
|
||||
member: '成员',
|
||||
batchImport: '批量导入',
|
||||
batchImportUser: '批量导入用户',
|
||||
downloadTemplate: '下载导入模板',
|
||||
templateDownloadSuccess: '模板下载成功',
|
||||
startImport: '开始导入',
|
||||
batchImportSuccess: '批量导入成功',
|
||||
importFailed: '导入失败,请检查文件格式',
|
||||
noFileSelected: '请选择要导入的文件',
|
||||
onlyXlsxOrCsv: '只能上传 .xlsx 或 .csv 格式的文件',
|
||||
reselect: '重新选择',
|
||||
noFileSelectedTip: '未选择任何文件',
|
||||
downloadTemplateTip: '请下载模板,填写用户信息后上传。'
|
||||
passwordRule: '密码至少需要6个字符',
|
||||
authVerify: '身份验证',
|
||||
authVerifyDesc: '出于安全考虑,请先验证您的登录密码',
|
||||
verify: '验证',
|
||||
loginPassword: '登录密码',
|
||||
loginPasswordPlaceholder: '请输入当前账号的登录密码',
|
||||
loginPasswordVerifyFailed: '密码错误,请重新输入',
|
||||
bindNewEmail: '绑定新邮箱',
|
||||
sureChange: '确认修改',
|
||||
sendEmailCode: '发送验证码',
|
||||
currentEmail: '当前邮箱',
|
||||
newEmail: '新邮箱地址',
|
||||
emailCode: '验证码',
|
||||
emailCodePlaceholder: '请输入新邮箱收到的验证码',
|
||||
sureChangeEmail: '确认将绑定邮箱修改为',
|
||||
sureChangeEmailDesc: '吗?',
|
||||
changeSuccess: '修改成功',
|
||||
sendSuccess: '验证码已发送,请查收',
|
||||
newEmailSameAsOld: '新邮箱不能与当前邮箱相同',
|
||||
emailCodeLengthRule: '请输入6位的验证码',
|
||||
emailFormatError: '邮箱格式不正确',
|
||||
sendCodeTooFrequent: '请在{{seconds}}s后重新发送',
|
||||
retrySend: '{{seconds}}s后可重发',
|
||||
},
|
||||
common: {
|
||||
search: '搜索',
|
||||
|
||||
219
web/src/views/UserManagement/components/ChangeEmailModal.tsx
Normal file
219
web/src/views/UserManagement/components/ChangeEmailModal.tsx
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-25 11:45:07
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-25 11:45:07
|
||||
*/
|
||||
/**
|
||||
* ChangeEmailModal Component
|
||||
*
|
||||
* A two-step modal for changing user email address with verification code.
|
||||
* Step 1: Enter new email and send verification code
|
||||
* Step 2: Confirm the email change
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input, App, Row, Col, Button, Steps } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ChangeEmailModalRef, ChangeEmailModalForm } from '../types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
import { changeEmail, sendEmailCode } from '@/api/user'
|
||||
import { useUser } from '@/store/user';
|
||||
import RbAlert from '@/components/RbAlert';
|
||||
import Empty from '@/components/Empty';
|
||||
import EmailIcon from '@/assets/images/login/email.svg'
|
||||
|
||||
const FormItem = Form.Item;
|
||||
|
||||
/**
|
||||
* Component props interface
|
||||
*/
|
||||
interface ChangeEmailModalProps {
|
||||
/** Callback function to refresh user data after email change */
|
||||
refresh: () => void;
|
||||
}
|
||||
|
||||
const steps = [
|
||||
'bindNewEmail',
|
||||
'sureChange',
|
||||
]
|
||||
|
||||
const ChangeEmailModal = forwardRef<ChangeEmailModalRef, ChangeEmailModalProps>(({
|
||||
refresh
|
||||
}, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const { message } = App.useApp();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [form] = Form.useForm<ChangeEmailModalForm>();
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [current, setCurrent] = useState<number>(0);
|
||||
const { user } = useUser();
|
||||
const [codeLoading, setCodeLoading] = useState(false)
|
||||
const [countdown, setCountdown] = useState(0)
|
||||
const newEmail = Form.useWatch(['new_email'], form)
|
||||
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
setCurrent(0)
|
||||
setCountdown(0)
|
||||
};
|
||||
/** Handle cancel button click - go back to previous step or close modal */
|
||||
const handleCancel = () => {
|
||||
if (current === 0) {
|
||||
handleClose()
|
||||
} else {
|
||||
setCurrent(0)
|
||||
}
|
||||
}
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
form.resetFields();
|
||||
setVisible(true);
|
||||
};
|
||||
/** Handle save/next button click - proceed to next step or submit email change */
|
||||
const handleSave = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
if (current === 0) {
|
||||
setCurrent(1)
|
||||
} else {
|
||||
setLoading(true)
|
||||
changeEmail(values)
|
||||
.then(() => {
|
||||
setLoading(false)
|
||||
refresh()
|
||||
handleClose()
|
||||
message.success(t('user.changeSuccess'))
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false)
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('err', err)
|
||||
});
|
||||
}
|
||||
|
||||
/** Send verification code to new email with countdown timer */
|
||||
const handleSendCode = () => {
|
||||
if (countdown > 0) {
|
||||
message.warning(t('user.sendCodeTooFrequent', { seconds: countdown }));
|
||||
return;
|
||||
}
|
||||
form
|
||||
.validateFields(['new_email'])
|
||||
.then((values) => {
|
||||
setCodeLoading(true)
|
||||
sendEmailCode({ email: values.new_email })
|
||||
.then(() => {
|
||||
message.success(t('user.sendSuccess'))
|
||||
setCountdown(300)
|
||||
const timer = setInterval(() => {
|
||||
setCountdown((prev) => {
|
||||
if (prev <= 1) {
|
||||
clearInterval(timer)
|
||||
return 0
|
||||
}
|
||||
return prev - 1
|
||||
})
|
||||
}, 1000)
|
||||
})
|
||||
.finally(() => {
|
||||
setCodeLoading(false)
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('err', err)
|
||||
});
|
||||
}
|
||||
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
}));
|
||||
|
||||
return (
|
||||
<RbModal
|
||||
title={t(`user.${steps[current]}`)}
|
||||
open={visible}
|
||||
onCancel={handleClose}
|
||||
footer={[
|
||||
<Button key="cancel" onClick={handleCancel}>{current === 1 ? t('common.prevStep') : t('common.cancel')}</Button>,
|
||||
<Button key="ok" loading={loading} type="primary" onClick={handleSave}>{current === 0 ? t('common.nextStep') : t('user.sureChange')}</Button>,
|
||||
]}
|
||||
>
|
||||
<div className='rb:p-3 rb:bg-[#FBFDFF] rb:rounded-lg rb:border rb:border-[#DFE4ED] rb:mb-3'>
|
||||
<Steps
|
||||
labelPlacement="vertical"
|
||||
size="small"
|
||||
current={current}
|
||||
items={steps.map(key => ({ title: t(`user.${key}`) }))}
|
||||
/>
|
||||
</div>
|
||||
{current === 0 && <RbAlert className="rb:mb-4!">{t('user.currentEmail')}: {user.email}</RbAlert>}
|
||||
{current === 1 && <Empty url={EmailIcon} size={80} isNeedSubTitle={false}
|
||||
title={<div className="rb:text-center">
|
||||
{t('user.sureChangeEmail')}<br />
|
||||
<div className="rb:font-medium rb:text-[#155EEF] rb:text-[16px]">{newEmail}</div>
|
||||
{t('user.sureChangeEmailDesc')}
|
||||
</div>} />}
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
hidden={current === 1}
|
||||
>
|
||||
<Row gutter={16} className="rb:mb-6!">
|
||||
<Col span={16}>
|
||||
<Form.Item
|
||||
name="new_email"
|
||||
label={t('user.newEmail')}
|
||||
rules={[
|
||||
{ required: true, message: t('common.pleaseEnter') },
|
||||
{ type: 'email', message: t('user.emailFormatError') },
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (value && value === user.email) {
|
||||
return Promise.reject(new Error(t('user.newEmailSameAsOld')));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
]}
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<Input placeholder={t('common.enter')} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Button
|
||||
className="rb:mt-7.5"
|
||||
disabled={countdown > 0}
|
||||
loading={codeLoading}
|
||||
onClick={handleSendCode}
|
||||
>{countdown > 0 ? t('user.retrySend', { seconds: countdown }) : t('user.sendEmailCode')}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<FormItem
|
||||
name="code"
|
||||
label={t('user.emailCode')}
|
||||
rules={[
|
||||
{ required: true, message: t('common.pleaseEnter') },
|
||||
{ len: 6, message: t('user.emailCodeLengthRule') }
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('user.emailCodePlaceholder')} />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</RbModal>
|
||||
);
|
||||
});
|
||||
|
||||
export default ChangeEmailModal;
|
||||
111
web/src/views/UserManagement/components/VerifyPasswordModal.tsx
Normal file
111
web/src/views/UserManagement/components/VerifyPasswordModal.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-25 10:51:17
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-25 11:46:11
|
||||
*/
|
||||
/**
|
||||
* VerifyPasswordModal Component
|
||||
*
|
||||
* A modal dialog for verifying user's current login password before performing
|
||||
* sensitive operations (e.g., changing email address).
|
||||
*/
|
||||
|
||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||
import { Form, Input } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ExclamationCircleFilled } from '@ant-design/icons';
|
||||
|
||||
import type { VerifyPasswordModalRef } from '../types'
|
||||
import RbModal from '@/components/RbModal'
|
||||
import { verifyPassword } from '@/api/user'
|
||||
import RbAlert from '@/components/RbAlert';
|
||||
|
||||
/**
|
||||
* VerifyPasswordModal component props
|
||||
*/
|
||||
interface VerifyPasswordModalProps {
|
||||
/** Callback function executed after successful password verification */
|
||||
refresh: () => void;
|
||||
}
|
||||
|
||||
const VerifyPasswordModal = forwardRef<VerifyPasswordModalRef, VerifyPasswordModalProps>(({ refresh }, ref) => {
|
||||
const { t } = useTranslation();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [form] = Form.useForm<{ password: string }>();
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
/** Close modal and reset form */
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
form.resetFields();
|
||||
setLoading(false)
|
||||
};
|
||||
|
||||
/** Open modal */
|
||||
const handleOpen = () => {
|
||||
form.resetFields();
|
||||
setVisible(true);
|
||||
};
|
||||
/** Verify password and execute callback on success */
|
||||
const handleSave = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
setLoading(true)
|
||||
verifyPassword(values)
|
||||
.then(() => {
|
||||
refresh()
|
||||
handleClose()
|
||||
})
|
||||
.catch(() => {
|
||||
form.setFields([{
|
||||
name: 'password',
|
||||
errors: [t('user.loginPasswordVerifyFailed')]
|
||||
}])
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('err', err)
|
||||
});
|
||||
}
|
||||
|
||||
/** Expose methods to parent component */
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleOpen,
|
||||
handleClose
|
||||
}));
|
||||
|
||||
return (
|
||||
<RbModal
|
||||
title={t('user.authVerify')}
|
||||
open={visible}
|
||||
onCancel={handleClose}
|
||||
okText={t('user.verify')}
|
||||
onOk={handleSave}
|
||||
confirmLoading={loading}
|
||||
>
|
||||
<RbAlert icon={<ExclamationCircleFilled />} className="rb:mb-4!">{ t('user.authVerifyDesc') }</RbAlert>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item
|
||||
name="password"
|
||||
label={t('user.loginPassword')}
|
||||
rules={[
|
||||
{ required: true, message: t('user.loginPasswordPlaceholder') },
|
||||
{ min: 6, message: t('user.passwordRule') }
|
||||
]}
|
||||
>
|
||||
<Input placeholder={t('user.loginPasswordPlaceholder')} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</RbModal>
|
||||
);
|
||||
});
|
||||
|
||||
export default VerifyPasswordModal;
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:50:56
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:51:17
|
||||
* @Last Modified time: 2026-02-25 11:44:02
|
||||
*/
|
||||
/**
|
||||
* User data type
|
||||
@@ -49,4 +49,33 @@ export interface CreateModalRef {
|
||||
*/
|
||||
export interface ResetPasswordModalRef {
|
||||
handleOpen: (user: User) => void;
|
||||
}
|
||||
/**
|
||||
* Verify password modal ref interface
|
||||
*/
|
||||
export interface VerifyPasswordModalRef {
|
||||
handleOpen: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check password modal ref interface
|
||||
*/
|
||||
export interface CheckPasswordModalRef {
|
||||
handleOpen: () => void;
|
||||
handleClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change email modal ref interface
|
||||
*/
|
||||
export interface ChangeEmailModalRef {
|
||||
handleOpen: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change email form data type
|
||||
*/
|
||||
export interface ChangeEmailModalForm {
|
||||
new_email: string;
|
||||
code: string;
|
||||
}
|
||||
Reference in New Issue
Block a user