Files
biz-mobile-cloud-zax/src/pages/root/forgot-password.vue
T

171 lines
5.5 KiB
Vue

<template>
<crud-layout title="找回密码" bg="/static/page/blue-bg.png">
<view class="h-30rpx" />
<view class="card p-x-30rpx p-y-40rpx">
<tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical">
<tm-form-item name="mobile" label="手机号" :showBottom="false">
<tm-input v-model="applyForm.mobile" :maxlength="11" placeholder="请输入手机号.." />
</tm-form-item>
<view class="h-30rpx" />
<tm-form-item name="smsCode" label="验证码" :showBottom="false">
<view class="flex items-center">
<view class="flex-1">
<tm-input v-model="applyForm.smsCode" :maxlength="6" placeholder="请输入短信验证码.." />
</view>
<view class="m-l-20rpx bg-blue p-x-20rpx p-y-12rpx rounded-20rpx text-center color-white font-size-sm"
:class="{ 'bg-grey': state.countdown > 0 }" @tap="sendSmsCode">
{{ smsButtonText }}
</view>
</view>
</tm-form-item>
<view class="h-30rpx" />
<tm-form-item name="newPassword" label="新密码" :showBottom="false">
<tm-input password showEye v-model="applyForm.newPassword" placeholder="请输入新密码.." />
</tm-form-item>
</tm-form>
</view>
<view class="bg-blue p-y-20rpx text-center rounded-40rpx m-x-30rpx m-t-40rpx sub-text-30 color-white!" @tap="doSubmit">
重置密码
</view>
</crud-layout>
</template>
<script setup lang="ts">
import { computed, onBeforeUnmount, reactive, ref, watch } from "vue"
import { userApi } from "@/sheep/api"
import { router } from "@@/index"
const MOBILE_REG = /^[1](([3][0-9])|([4][0,1,4-9])|([5][0-3,5-9])|([6][2,5,6,7])|([7][0-8])|([8][0-9])|([9][0-3,5-9]))[0-9]{8}$/
const SMS_CODE_REG = /^\d{6}$/
const PASSWORD_REG = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9]{8,20}$/
const applyForm = reactive({
mobile: "",
smsCode: "",
newPassword: "",
})
const applyFormRef = ref<any>(null)
const state = reactive({
countdown: 0,
sendingSms: false,
submitting: false,
})
let countdownTimer: ReturnType<typeof setInterval> | null = null
const applyFormRules = reactive<any>({
mobile: [
{ required: true, message: "请输入手机号" },
{ required: true, rule: MOBILE_REG, message: "请输入正确的手机号" },
],
smsCode: [
{ required: true, message: "请输入验证码" },
{ required: true, rule: SMS_CODE_REG, message: "验证码必须为6位数字" },
],
newPassword: [
{ required: true, message: "请输入新密码" },
{ required: true, rule: PASSWORD_REG, message: "密码需为8-20位字母和数字组合" },
],
})
const smsButtonText = computed(() => (state.countdown > 0 ? `${state.countdown}s后重发` : "发送验证码"))
watch(
() => applyForm.mobile,
(value) => {
const normalized = (value || "").replace(/\D/g, "").slice(0, 11)
if (normalized !== value) {
applyForm.mobile = normalized
}
},
)
watch(
() => applyForm.smsCode,
(value) => {
const normalized = (value || "").replace(/\D/g, "").slice(0, 6)
if (normalized !== value) {
applyForm.smsCode = normalized
}
},
)
const startCountdown = () => {
state.countdown = 60
clearCountdown()
countdownTimer = setInterval(() => {
state.countdown -= 1
if (state.countdown <= 0) {
clearCountdown()
}
}, 1000)
}
const clearCountdown = () => {
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
}
const sendSmsCode = async () => {
if (state.countdown > 0 || state.sendingSms) return
if (!applyForm.mobile) {
uni.showToast({ title: "请输入手机号", icon: "none" })
return
}
if (!MOBILE_REG.test(applyForm.mobile)) {
uni.showToast({ title: "请输入正确的手机号", icon: "none" })
return
}
state.sendingSms = true
const res: any = await userApi.sendForgotPasswordSmsCode(applyForm.mobile)
state.sendingSms = false
if (res.code !== 0 || !res.data) {
uni.showToast({ title: res.msg || "验证码发送失败,请稍后重试", icon: "none" })
return
}
uni.showToast({ title: "验证码已发送", icon: "none" })
startCountdown()
}
const doSubmit = async () => {
if (state.submitting) return
applyFormRef.value.submit()
const { isPass } = applyFormRef.value.validate()
if (!isPass) return
state.submitting = true
uni.showLoading({ title: "重置中...", mask: true })
const res: any = await userApi.resetForgotPassword({
mobile: applyForm.mobile,
smsCode: applyForm.smsCode,
newPassword: applyForm.newPassword,
})
uni.hideLoading()
state.submitting = false
if (res.code !== 0 || !res.data) {
uni.showToast({ title: res.msg || "密码重置失败,请稍后重试", icon: "none" })
return
}
uni.showToast({ title: "密码重置成功,请重新登录", icon: "none" })
router.jump("/pages/root/login", {}, 2)
}
onBeforeUnmount(() => {
clearCountdown()
})
</script>
<style lang="scss" scoped>
.bg-grey {
background-color: #aeb3bf !important;
}
:deep() {
.tmFormItemLabel {
padding-bottom: 10rpx !important;
}
}
</style>