171 lines
5.5 KiB
Vue
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>
|