feat: 优化忘记密码页面 - 移除步骤条,合并表单,验证码后才能输入密码

This commit is contained in:
2026-05-26 16:20:37 +08:00
parent a1205c3d5a
commit b6d968ae96
4 changed files with 131 additions and 229 deletions

View File

@ -153,7 +153,7 @@ const transferType = ref('internal')
* @description 创建默认转账表单数据 * @description 创建默认转账表单数据
*/ */
const createDefaultTransferForm = ():any => ({ const createDefaultTransferForm = ():any => ({
transferOutAccount: -1, transferOutAccount: '',
transferInAccount: '', transferInAccount: '',
transferAmount: '', transferAmount: '',
remark: '', remark: '',
@ -357,6 +357,8 @@ const getTransferWalletOptions = async () => {
const data: any = await getTransferWalletOptionsApi() const data: any = await getTransferWalletOptionsApi()
transferFormOptionsMap.value.transferOutAccount = data transferFormOptionsMap.value.transferOutAccount = data
transferFormOptionsMap.value.transferInAccount = data.filter((item: any) => item.value !== -1) transferFormOptionsMap.value.transferInAccount = data.filter((item: any) => item.value !== -1)
transferForm.value.transferOutAccount = -1
} }
/** /**
* @description 页面加载完成需要渲染的数据 * @description 页面加载完成需要渲染的数据

View File

@ -41,23 +41,18 @@ import { getStorage } from '@/utils/storage'
import dayjs from 'dayjs' import dayjs from 'dayjs'
// //
import { getRecentSevenDays } from '@/utils/handleTime.ts'
/** /**
* @description: 定义搜索数据 * @description: 定义搜索数据
*/ */
// //
const dateRange = { const initTime = getRecentSevenDays()
start: dayjs().subtract(7, 'day').format('YYYY-MM-DD'), const searchForm = ref<any>({
end: dayjs().format('YYYY-MM-DD'),
}
const searchForm = ref({
accountName: '', accountName: '',
orderId: '2', orderId: '2',
tradeType: '', tradeType: '',
start_time: dateRange.start, dateRange: [initTime.start, initTime.end],
end_time: dateRange.end,
startTime: dateRange.start,
endTime: dateRange.end,
subAccountName: '', subAccountName: '',
}) })
const searchOptions = ref(search) const searchOptions = ref(search)
@ -85,6 +80,13 @@ const total = ref(0)
const getCustomerTradeOrderList = async () => { const getCustomerTradeOrderList = async () => {
try { try {
startLoading() startLoading()
// dateRange
let startTime = initTime.start
let endTime = initTime.end
if (searchForm.value.dateRange && searchForm.value.dateRange.length === 2) {
startTime = dayjs(searchForm.value.dateRange[0]).format('YYYY-MM-DD HH:mm:ss')
endTime = dayjs(searchForm.value.dateRange[1]).format('YYYY-MM-DD HH:mm:ss')
}
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -92,8 +94,8 @@ const getCustomerTradeOrderList = async () => {
account_id: searchForm.value.subAccountName, account_id: searchForm.value.subAccountName,
order_no: '', order_no: '',
type: searchForm.value.tradeType, type: searchForm.value.tradeType,
start_time: dateRange.start, start_time: startTime,
end_time: dateRange.end, end_time: endTime,
} }
console.log(params) console.log(params)
const data: any = await getCustomTradeOrderListApi(params) const data: any = await getCustomTradeOrderListApi(params)
@ -165,10 +167,7 @@ const handleReset = () => {
accountName: '', accountName: '',
orderId: '', orderId: '',
tradeType: '', tradeType: '',
startTime: dateRange.start, dateRange: [initTime.start, initTime.end],
endTime: dateRange.end,
start_time: dateRange.start,
end_time: dateRange.end,
subAccountName: '', subAccountName: '',
} }
getCustomerTradeOrderList() getCustomerTradeOrderList()

View File

@ -57,18 +57,19 @@ export const search = [
], ],
width: '140px' width: '140px'
}, },
// // 开始时间 // 时间范围
// { {
// prop: "startTime", label: '注册开始时间', type: 'date' as const, dateType: 'date' as const, // 时间范围选择器 label: '时间',
// valueFormat: 'YYYY-MM-DD', prop: 'dateRange',
// width: '140px' type: 'date' as const,
// }, dateType: 'datetimerange' as const,
// // 结束时间 placeholder: '请选择时间范围',
// { format: 'YYYY-MM-DD HH:mm:ss',
// prop: "endTime", label: '注册结束时间', type: 'date' as const, dateType: 'date' as const, // 时间范围选择器 valueFormat: 'YYYY-MM-DD HH:mm:ss',
// valueFormat: 'YYYY-MM-DD', startPlaceholder: '开始时间',
// width: '140px' endPlaceholder: '结束时间',
// }, width: '340'
},
] ]
/** /**

View File

@ -2,46 +2,44 @@
<div class="user-card"> <div class="user-card">
<UserHeader /> <UserHeader />
<UserText :user-text="userText" @back="handleBack" /> <UserText :user-text="userText" @back="handleBack" />
<UserTabs v-model="activeTab" :hide-tab="hideTab" :tab-list="tabList" /> <UserTabs v-model="activeTab" :hide-tab="false" :tab-list="tabList" />
<el-form class="user-form" ref="forgotPasswordFormRef" :model="forgotPasswordForm" :rules="forgotPasswordRules"> <el-form class="user-form" ref="forgotPasswordFormRef" :model="forgotPasswordForm" :rules="forgotPasswordRules">
<Transition name="form-switch" mode="out-in"> <!-- 邮箱输入 -->
<el-form-item v-if="activeTab === 'email'" key="form-email-item" prop="email"> <el-form-item prop="email" v-if="activeTab === 'email'">
<el-input v-model="forgotPasswordForm.email" placeholder="请输入邮箱" clearable size="large" /> <el-input v-model="forgotPasswordForm.email" placeholder="请输入邮箱" clearable size="large" />
</el-form-item> </el-form-item>
<!-- 手机号输入 -->
<el-form-item class="phone-item" prop="phone" v-else-if="activeTab === 'phone'">
<el-select v-model="forgotPasswordForm.countryCode" :teleported="false" value-key="code" placeholder="国家"
size="large">
<el-option v-for="item in countryOptions" :key="item.code" :label="item.code" :value="item.code">
<div class="country-option">
<span class="country-name">{{ item.name }}</span>
<span class="country-code">{{ item.code }}</span>
</div>
</el-option>
</el-select>
<el-input v-model="forgotPasswordForm.phone" placeholder="请输入手机号" clearable size="large" />
</el-form-item>
<!-- 验证码输入 -->
<UserCaptcha ref="captchaRef" v-model="forgotPasswordForm.code" @sendCode="handleSendCode" :show-send-button="showSendButton"
:send-state="sendState" />
<!-- 密码输入验证码发送后才可输入 -->
<el-form-item prop="password">
<el-input v-model="forgotPasswordForm.password" :placeholder="codeSent ? '请输入新密码' : '请先发送验证码'"
:disabled="!codeSent" type="password" show-password clearable size="large" />
</el-form-item>
<el-form-item prop="confirmPassword">
<el-input v-model="forgotPasswordForm.confirmPassword" :placeholder="codeSent ? '请确认新密码' : '请先发送验证码'"
:disabled="!codeSent" type="password" show-password clearable size="large" />
</el-form-item>
<el-form-item class="phone-item" prop="phone" v-else-if="activeTab === 'phone'" key="form-phone-item">
<el-select v-model="forgotPasswordForm.countryCode" :teleported="false" value-key="code" placeholder="国家"
size="large">
<el-option v-for="item in countryOptions" :key="item.code" :label="item.code" :value="item.code">
<div class="country-option">
<span class="country-name">{{ item.name }}</span>
<span class="country-code">{{ item.code }}</span>
</div>
</el-option>
</el-select>
<el-input v-model="forgotPasswordForm.phone" placeholder="请输入手机号" clearable size="large" />
</el-form-item>
<UserCaptcha v-model="forgotPasswordForm.code" v-else-if="activeTab === 'code'" key="form-code-item"
@sendCode="handleSendCode" :show-send-button="showSendButton" :send-state="sendState" />
<div v-else-if="activeTab === 'password'" key="form-password-group">
<el-form-item prop="password">
<el-input v-model="forgotPasswordForm.password" placeholder="请输入新密码" type="password" show-password clearable
size="large" />
</el-form-item>
<el-form-item prop="confirmPassword">
<el-input v-model="forgotPasswordForm.confirmPassword" placeholder="请确认新密码" type="password" show-password
clearable size="large" />
</el-form-item>
</div>
</Transition>
<div class="forgot-next">
<div :class="{ active: activeTab === 'email' || activeTab === 'phone' }"></div>
<div :class="{ active: activeTab === 'code' }"></div>
<div :class="{ active: activeTab === 'password' }"></div>
</div>
<el-form-item class="form-submit"> <el-form-item class="form-submit">
<el-button type="primary" @click="handleForgotPassword">{{ activeTab === 'password' ? '去登录' : '继续' <el-button type="primary" @click="handleForgotPassword">{{ codeSent ? '重置密码' : '发送验证码'
}}</el-button> }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -78,22 +76,24 @@ const router = useRouter()
//userText //userText
const userText = ref({ const userText = ref({
backText: '返回', backText: '返回',
title: '恢复密码', title: '忘记密码',
text: '请输入您注册时使用的邮箱或手机号', text: '请输入您注册时使用的邮箱或手机号',
account: '', account: '',
}) })
//tabs // tabs -
const tabList = ref([ const tabList = ref([
{ label: '邮箱', name: 'email' }, { label: '邮箱', name: 'email' },
{ label: '手机', name: 'phone' }, { label: '手机', name: 'phone' },
]) ])
const activeTab = ref<string>('email') const activeTab = ref<string>('email')
const hideTab = ref<boolean>(false)
//
const codeSent = ref(false)
// //
const forgotPasswordFormRef = ref<FormInstance>() const forgotPasswordFormRef = ref<FormInstance>()
const captchaRef = ref()
const forgotPasswordForm = ref({ const forgotPasswordForm = ref({
email: '', email: '',
countryCode: '+86', countryCode: '+86',
@ -110,13 +110,31 @@ const forgotPasswordRules = ref<FormRules>({
code: codeRules().code code: codeRules().code
}) })
// activeTab
watch(activeTab, (newTab, oldTab) => {
codeSent.value = false
forgotPasswordForm.value.password = ''
forgotPasswordForm.value.confirmPassword = ''
//
captchaRef.value?.clear()
//
if (newTab === 'phone') {
forgotPasswordForm.value.email = ''
forgotPasswordFormRef.value?.clearValidate(['email'])
} else if (newTab === 'email') {
forgotPasswordForm.value.phone = ''
forgotPasswordFormRef.value?.clearValidate(['phone'])
}
forgotPasswordFormRef.value?.clearValidate(['code', 'password', 'confirmPassword'])
})
// //
const showSendButton = ref<boolean>(true) const showSendButton = ref<boolean>(true)
const sendState = ref<object>({ const sendState = ref<object>({
isCounting: false, isCounting: false,
countdown: 0, countdown: 0,
}) })
// //
const emailCaptchaState = initCaptchaState('email', 60, 'forgot_email'); // const emailCaptchaState = initCaptchaState('email', 60, 'forgot_email'); //
const phoneCaptchaState = initCaptchaState('phone', 60, 'forgot_phone'); // const phoneCaptchaState = initCaptchaState('phone', 60, 'forgot_phone'); //
// //
@ -126,7 +144,6 @@ const { isCounting: phoneIsCounting, countdown: phoneCountdown } = phoneCaptchaS
// //
// sendState // sendState
const updateSendState = (type: string) => { const updateSendState = (type: string) => {
if (type === 'email') { if (type === 'email') {
@ -142,167 +159,88 @@ const updateSendState = (type: string) => {
} }
} }
// activeTabsendState
watch(activeTab, (newTab) => {
if (newTab === 'email' || newTab === 'phone') {
// /sendState
sendState.value = { isCounting: false, countdown: 0 }
} else if (newTab === 'code') {
//
const currentType: string = getStorage('forgotType', 'session') || 'email'
updateSendState(currentType)
}
})
// //
const sendCodeApi = async () => { const sendCodeApi = async () => {
// //
const sendParams = { const sendParams = {
email: forgotPasswordForm.value.email, email: forgotPasswordForm.value.email,
mobile: forgotPasswordForm.value.phone, mobile: forgotPasswordForm.value.phone,
type:'transfer_code' type: 'transfer_code'
} }
return userStore.sendCode(sendParams) return userStore.sendCode(sendParams)
} }
// //
const handleSendCode = async () => { const handleSendCode = async () => {
// // tab
const type: string = getStorage('forgotType', 'session') || 'email' const type = activeTab.value
const receiver = type === 'phone' ? forgotPasswordForm.value.phone : forgotPasswordForm.value.email; const receiver = type === 'phone' ? forgotPasswordForm.value.phone : forgotPasswordForm.value.email;
const currentCaptchaState = type === 'phone' ? phoneCaptchaState : emailCaptchaState; const currentCaptchaState = type === 'phone' ? phoneCaptchaState : emailCaptchaState;
// //
await sendCaptcha( await sendCaptcha(
type, type,
receiver, receiver,
currentCaptchaState, // currentCaptchaState,
() => sendCodeApi() // Promise () => sendCodeApi()
); );
// //
updateSendState(type) updateSendState(type)
//
codeSent.value = true
} }
// //
const handleBack = () => { const handleBack = () => {
// router.push('/user/login')
if (activeTab.value === 'password') {
activeTab.value = 'code'
userText.value = {
...userText.value,
title: '双重验证',
text: '请输入发送的验证码',
account: getStorage('forgot', 'session') || ''
}
//
forgotPasswordForm.value.password = ''
forgotPasswordForm.value.confirmPassword = ''
forgotPasswordFormRef.value?.clearValidate(['password', 'confirmPassword'])
}
// /
else if (activeTab.value === 'code') {
const getForgotType: string = getStorage('forgotType', 'session') || 'email'
activeTab.value = getForgotType
hideTab.value = false
userText.value = {
...userText.value,
title: '恢复密码',
text: '请输入您注册时使用的邮箱或手机号',
account: ''
}
//
forgotPasswordForm.value.code = ''
forgotPasswordFormRef.value?.clearValidate(['code'])
}
// /
else {
activeTab.value = 'email'
forgotPasswordForm.value.email = ''
forgotPasswordForm.value.phone = ''
forgotPasswordFormRef.value?.clearValidate(['email', 'phone'])
router.push('/user/login')
}
} }
// //
const handleForgotPassword = async () => { const handleForgotPassword = async () => {
if (!forgotPasswordFormRef.value) return if (!forgotPasswordFormRef.value) return
try { try {
const currentTab = activeTab.value; // tab
const getForgotType: string = getStorage('forgotType', 'session') || 'email' await forgotPasswordFormRef.value.validateField(activeTab.value)
// 1/
if (currentTab === 'email' || currentTab === 'phone') { //
// await forgotPasswordFormRef.value.validateField('code')
await forgotPasswordFormRef.value.validateField(currentTab)
// //
setStorage('forgotType', currentTab, 'session') await forgotPasswordFormRef.value.validateField(['password', 'confirmPassword'])
setStorage('forgot', currentTab === 'email' ? forgotPasswordForm.value.email : forgotPasswordForm.value.phone, 'session')
// //
hideTab.value = true const forgotParams = {
activeTab.value = 'code' mobile: activeTab.value === 'phone' ? forgotPasswordForm.value.phone : undefined,
userText.value = { email: activeTab.value === 'email' ? forgotPasswordForm.value.email : undefined,
...userText.value, code: forgotPasswordForm.value.code,
title: '双重验证', login_password1: forgotPasswordForm.value.password,
text: '请输入发送的验证码', login_password2: forgotPasswordForm.value.confirmPassword,
account: currentTab === 'email' ? forgotPasswordForm.value.email : forgotPasswordForm.value.phone
}
updateSendState(getForgotType)
} }
// 2 //
else if (currentTab === 'code') { await userStore.forgotPassword(forgotParams)
//
await forgotPasswordFormRef.value.validateField(currentTab) //
// forgotPasswordForm.value = {
activeTab.value = 'password' email: '',
userText.value = { countryCode: '+86',
...userText.value, phone: '',
title: '设置新密码', password: '',
text: '请设置您的新登录密码', confirmPassword: '',
account: getStorage('forgot', 'session') || '' code: ''
}
} }
codeSent.value = false
// 3 //
else if (currentTab === 'password') { ElMessage.success('密码重置成功,请登录')
// + router.push('/user/login')
await forgotPasswordFormRef.value.validateField(['password', 'confirmPassword'])
//
const forgotParams = {
mobile: forgotPasswordForm.value.phone,
email: forgotPasswordForm.value.email,
code: forgotPasswordForm.value.code,
// login_password1: md5Encrypt(forgotPasswordForm.value.password),
// login_password2: md5Encrypt(forgotPasswordForm.value.confirmPassword),
login_password1: forgotPasswordForm.value.password,
login_password2: forgotPasswordForm.value.confirmPassword,
}
//
await userStore.forgotPassword(forgotParams)
//
removeStorage('forgotType', 'session')
removeStorage('forgot', 'session')
forgotPasswordForm.value = {
email: '',
countryCode: '+86',
phone: '',
password: '',
confirmPassword: '',
code: ''
}
}
} catch (error) { } catch (error) {
console.error('表单提交失败:', error) console.error('表单提交失败:', error)
} }
} }
// 4. //
onUnmounted(() => { onUnmounted(() => {
clearCaptchaTimer(emailCaptchaState); clearCaptchaTimer(emailCaptchaState);
clearCaptchaTimer(phoneCaptchaState); clearCaptchaTimer(phoneCaptchaState);
@ -316,43 +254,5 @@ onUnmounted(() => {
flex-wrap: nowrap; flex-wrap: nowrap;
} }
} }
.forgot-next {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 20px;
margin-top: 60px;
div {
position: relative;
height: 4px;
width: 40px;
background: rgb(209 213 219 / var(--tw-bg-opacity, 1));
border-radius: 999px;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 0;
background-color: var(--el-color-primary);
transition: all 0.3s ease-out;
border-radius: 999px;
}
&:nth-child(2) {
margin: 0 10px;
}
}
.active {
&::before {
width: 100%;
}
}
}
} }
</style> </style>