|
@@ -8,9 +8,9 @@
|
|
|
</route>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-// 必须导入需要用到的页面生命周期(即使在当前页面上没有直接使用到)
|
|
|
-// eslint-disable-next-line unused-imports/no-unused-imports
|
|
|
-import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
|
|
|
+import { getCode, updateUserPassword } from '@/api/login'
|
|
|
+import { goBack as goBackUtil, toPage } from '@/utils/page'
|
|
|
+import { toast } from '@/utils/toast'
|
|
|
|
|
|
defineOptions({
|
|
|
name: 'ForgotPassword', // 忘记密码
|
|
@@ -25,121 +25,159 @@ const step = ref(1)
|
|
|
|
|
|
// 表单数据
|
|
|
const formData = ref({
|
|
|
- mobile: '',
|
|
|
- verificationCode: '',
|
|
|
- newPassword: '',
|
|
|
- confirmPassword: '',
|
|
|
+ phone: '',
|
|
|
+ verifyCode: '',
|
|
|
+ newPwd: '',
|
|
|
+ confirmPwd: '',
|
|
|
})
|
|
|
|
|
|
// 验证码倒计时
|
|
|
const countdown = ref(0)
|
|
|
-const countdownTimer = ref<NodeJS.Timeout | null>(null)
|
|
|
-
|
|
|
-// 表单验证规则
|
|
|
-const rules = {
|
|
|
- mobile: [
|
|
|
- { required: true, message: 'Please enter mobile number' },
|
|
|
- { pattern: /^1[3-9]\d{9}$/, message: 'Please enter valid mobile number' },
|
|
|
- ],
|
|
|
- verificationCode: [
|
|
|
- { required: true, message: 'Please enter verification code' },
|
|
|
- ],
|
|
|
- newPassword: [
|
|
|
- { required: true, message: 'Please enter new password' },
|
|
|
- { min: 6, max: 20, message: 'Password should be 6-20 characters' },
|
|
|
- ],
|
|
|
- confirmPassword: [
|
|
|
- { required: true, message: 'Please confirm password' },
|
|
|
- {
|
|
|
- validator: (value: string) => {
|
|
|
- return value === formData.value.newPassword
|
|
|
- },
|
|
|
- message: 'Passwords do not match',
|
|
|
- },
|
|
|
- ],
|
|
|
-}
|
|
|
+const countdownTimer = ref<any>(null)
|
|
|
|
|
|
// 获取验证码
|
|
|
-function getVerificationCode() {
|
|
|
- if (!formData.value.mobile) {
|
|
|
- uni.showToast({
|
|
|
- title: 'Please enter mobile number first',
|
|
|
- icon: 'none',
|
|
|
- })
|
|
|
+async function getVerificationCode() {
|
|
|
+ // 验证手机号
|
|
|
+ if (!formData.value.phone.trim()) {
|
|
|
+ toast.error('Please enter phone number first')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- // TODO: 实现获取验证码逻辑
|
|
|
- console.log('Get verification code for:', formData.value.mobile)
|
|
|
+ // 验证手机号格式
|
|
|
+ const phoneRegex = /^1[3-9]\d{9}$/
|
|
|
+ if (!phoneRegex.test(formData.value.phone)) {
|
|
|
+ toast.error('Please enter valid phone number')
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- // 开始倒计时
|
|
|
- countdown.value = 60
|
|
|
- countdownTimer.value = setInterval(() => {
|
|
|
- countdown.value--
|
|
|
- if (countdown.value <= 0) {
|
|
|
- clearInterval(countdownTimer.value!)
|
|
|
- countdownTimer.value = null
|
|
|
- }
|
|
|
- }, 1000)
|
|
|
+ // 防止重复点击
|
|
|
+ if (countdown.value > 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 显示加载状态
|
|
|
+ uni.showLoading({
|
|
|
+ title: 'Sending...',
|
|
|
+ mask: true,
|
|
|
+ })
|
|
|
+
|
|
|
+ // 调用获取验证码接口
|
|
|
+ await getCode(formData.value.phone)
|
|
|
+
|
|
|
+ uni.hideLoading()
|
|
|
+ toast.success('Verification code sent successfully')
|
|
|
+
|
|
|
+ // 开始倒计时
|
|
|
+ countdown.value = 60
|
|
|
+ countdownTimer.value = setInterval(() => {
|
|
|
+ countdown.value--
|
|
|
+ if (countdown.value <= 0) {
|
|
|
+ clearInterval(countdownTimer.value!)
|
|
|
+ countdownTimer.value = null
|
|
|
+ }
|
|
|
+ }, 1000)
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ uni.hideLoading()
|
|
|
+ toast.error(error.message || 'Failed to send verification code')
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 第一步:验证手机号
|
|
|
function handleStep1() {
|
|
|
- if (!formData.value.mobile) {
|
|
|
- uni.showToast({
|
|
|
- title: 'Please enter mobile number',
|
|
|
- icon: 'none',
|
|
|
- })
|
|
|
+ // 验证手机号
|
|
|
+ if (!formData.value.phone.trim()) {
|
|
|
+ toast.error('Please enter phone number')
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- // TODO: 验证手机号逻辑
|
|
|
- console.log('Verify mobile:', formData.value.mobile)
|
|
|
+ // 验证手机号格式
|
|
|
+ const phoneRegex = /^1[3-9]\d{9}$/
|
|
|
+ if (!phoneRegex.test(formData.value.phone)) {
|
|
|
+ toast.error('Please enter valid phone number')
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
// 跳转到第二步
|
|
|
step.value = 2
|
|
|
- // 预填手机号
|
|
|
- formData.value.mobile = '88016266123'
|
|
|
}
|
|
|
|
|
|
// 第二步:重置密码
|
|
|
-function handleResetPassword() {
|
|
|
- if (!formData.value.verificationCode || !formData.value.newPassword || !formData.value.confirmPassword) {
|
|
|
- uni.showToast({
|
|
|
- title: 'Please fill in all fields',
|
|
|
- icon: 'none',
|
|
|
+async function handleResetPassword() {
|
|
|
+ try {
|
|
|
+ // 表单验证
|
|
|
+ const isValid = await validateResetForm()
|
|
|
+ if (!isValid)
|
|
|
+ return
|
|
|
+
|
|
|
+ // 显示加载状态
|
|
|
+ uni.showLoading({
|
|
|
+ title: 'Resetting password...',
|
|
|
+ mask: true,
|
|
|
})
|
|
|
- return
|
|
|
- }
|
|
|
|
|
|
- if (formData.value.newPassword !== formData.value.confirmPassword) {
|
|
|
- uni.showToast({
|
|
|
- title: 'Passwords do not match',
|
|
|
- icon: 'none',
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
+ // 调用重置密码接口
|
|
|
+ const resetData = {
|
|
|
+ newPwd: formData.value.newPwd,
|
|
|
+ verifyCode: formData.value.verifyCode,
|
|
|
+ }
|
|
|
|
|
|
- // TODO: 实现重置密码逻辑
|
|
|
- console.log('Reset password:', formData.value)
|
|
|
+ await updateUserPassword(resetData)
|
|
|
|
|
|
- uni.showToast({
|
|
|
- title: 'Password reset successfully',
|
|
|
- icon: 'success',
|
|
|
- })
|
|
|
+ uni.hideLoading()
|
|
|
+ toast.success('Password reset successfully')
|
|
|
|
|
|
- // 跳转到登录页
|
|
|
- setTimeout(() => {
|
|
|
- uni.redirectTo({
|
|
|
- url: '/pages/login/login',
|
|
|
- })
|
|
|
- }, 1500)
|
|
|
+ // 跳转到登录页
|
|
|
+ setTimeout(() => {
|
|
|
+ toPage('/pages/login/login', {}, true)
|
|
|
+ }, 1500)
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ uni.hideLoading()
|
|
|
+ toast.error(error.message || 'Failed to reset password')
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-// 跳转登录页
|
|
|
-function toLogin() {
|
|
|
- uni.navigateTo({
|
|
|
- url: '/pages/login/login',
|
|
|
+// 重置密码表单验证
|
|
|
+function validateResetForm() {
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ // 验证验证码
|
|
|
+ if (!formData.value.verifyCode.trim()) {
|
|
|
+ toast.error('Please enter verification code')
|
|
|
+ resolve(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证新密码
|
|
|
+ if (!formData.value.newPwd.trim()) {
|
|
|
+ toast.error('Please enter new password')
|
|
|
+ resolve(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证密码长度
|
|
|
+ if (formData.value.newPwd.length < 6 || formData.value.newPwd.length > 20) {
|
|
|
+ toast.error('Password should be 6-20 characters')
|
|
|
+ resolve(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证确认密码
|
|
|
+ if (!formData.value.confirmPwd.trim()) {
|
|
|
+ toast.error('Please confirm your password')
|
|
|
+ resolve(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证两次密码是否一致
|
|
|
+ if (formData.value.newPwd !== formData.value.confirmPwd) {
|
|
|
+ toast.error('Passwords do not match')
|
|
|
+ resolve(false)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ resolve(true)
|
|
|
})
|
|
|
}
|
|
|
|
|
@@ -149,7 +187,7 @@ function goBack() {
|
|
|
step.value = 1
|
|
|
}
|
|
|
else {
|
|
|
- uni.navigateBack()
|
|
|
+ goBackUtil()
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -186,11 +224,11 @@ onUnmounted(() => {
|
|
|
|
|
|
<!-- 第一步:输入手机号 -->
|
|
|
<view v-if="step === 1">
|
|
|
- <wd-form ref="form" :model="formData" :rules="rules">
|
|
|
+ <wd-form ref="form" :model="formData">
|
|
|
<view class="mb-60rpx">
|
|
|
<wd-input
|
|
|
- v-model="formData.mobile"
|
|
|
- prop="mobile"
|
|
|
+ v-model="formData.phone"
|
|
|
+ prop="phone"
|
|
|
placeholder="+88 Mobile number"
|
|
|
no-border
|
|
|
custom-class="bandhu-auth-input-field"
|
|
@@ -211,19 +249,19 @@ onUnmounted(() => {
|
|
|
|
|
|
<!-- 第二步:重置密码 -->
|
|
|
<view v-else>
|
|
|
- <wd-form ref="form" :model="formData" :rules="rules">
|
|
|
+ <wd-form ref="form" :model="formData">
|
|
|
<view class="mb-40rpx space-y-32rpx">
|
|
|
<wd-input
|
|
|
- v-model="formData.mobile"
|
|
|
- prop="mobile"
|
|
|
-
|
|
|
- no-border disabled
|
|
|
+ v-model="formData.phone"
|
|
|
+ prop="phone"
|
|
|
+ no-border
|
|
|
+ readonly
|
|
|
custom-class="bandhu-auth-input-field"
|
|
|
/>
|
|
|
<view class="flex items-center gap-20rpx">
|
|
|
<wd-input
|
|
|
- v-model="formData.verificationCode"
|
|
|
- prop="verificationCode"
|
|
|
+ v-model="formData.verifyCode"
|
|
|
+ prop="verifyCode"
|
|
|
placeholder="Verification Code"
|
|
|
no-border
|
|
|
custom-class="flex-1 bandhu-auth-input-field"
|
|
@@ -239,16 +277,16 @@ onUnmounted(() => {
|
|
|
</wd-button>
|
|
|
</view>
|
|
|
<wd-input
|
|
|
- v-model="formData.newPassword"
|
|
|
- prop="newPassword"
|
|
|
+ v-model="formData.newPwd"
|
|
|
+ prop="newPwd"
|
|
|
|
|
|
placeholder="New Password 6-20 characters"
|
|
|
no-border show-password
|
|
|
custom-class="bandhu-auth-input-field"
|
|
|
/>
|
|
|
<wd-input
|
|
|
- v-model="formData.confirmPassword"
|
|
|
- prop="confirmPassword"
|
|
|
+ v-model="formData.confirmPwd"
|
|
|
+ prop="confirmPwd"
|
|
|
show-password
|
|
|
placeholder="Confirm password"
|
|
|
no-border
|
|
@@ -278,7 +316,7 @@ onUnmounted(() => {
|
|
|
<text class="text-28rpx text-#666">
|
|
|
Already have account?
|
|
|
</text>
|
|
|
- <text class="text-28rpx text-[var(--wot-color-theme)]" @click="toLogin">
|
|
|
+ <text class="text-28rpx text-[var(--wot-color-theme)]" @click="toPage('/pages/login/login')">
|
|
|
Login Now
|
|
|
</text>
|
|
|
</view>
|