369 lines
11 KiB
Vue
369 lines
11 KiB
Vue
![]() |
<template>
|
||
|
<NavBar />
|
||
|
<view class="container">
|
||
|
<view class="welcomeTitle">{{ $t('login.pageDescription') }}</view>
|
||
|
<view class="forgotPWDText" @click="backToLogin">
|
||
|
<uni-icons type="left" size="20"></uni-icons>
|
||
|
<text>{{ $t('common.forgotPassword') }}</text>
|
||
|
</view>
|
||
|
<view class="stepList">
|
||
|
<view v-for="(step, index) in stepList" :key="step.title" class="stepItem">
|
||
|
<view class="numWrapper">
|
||
|
<view v-if="index > 0" class="spaceLine"></view>
|
||
|
<view :class="['num', active === index ? 'active' : '']">
|
||
|
{{ index + 1 }}
|
||
|
</view>
|
||
|
</view>
|
||
|
<view class="stepTitle">{{ step.title }}</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view v-show="active === 0">
|
||
|
<uni-forms ref="verifyIdentityForm" :modelValue="verifyIdentityFormData" :rules="verifyIdentityFormRules">
|
||
|
<uni-forms-item name="email">
|
||
|
<view class="forgotPWDInputWrapper">
|
||
|
<image src="/static/email.png" mode="aspectFit" class="icon"></image>
|
||
|
<view class="divider"></view>
|
||
|
<uni-easyinput
|
||
|
trim="all"
|
||
|
primaryColor="#29BBE4"
|
||
|
:input-border="false"
|
||
|
v-model="verifyIdentityFormData.email"
|
||
|
:placeholder="$t('form.registerEmail.placeholder')"
|
||
|
></uni-easyinput>
|
||
|
</view>
|
||
|
</uni-forms-item>
|
||
|
<uni-forms-item name="code">
|
||
|
<view class="forgotPWDInputWrapper">
|
||
|
<image src="/static/send.png" mode="aspectFit" class="icon"></image>
|
||
|
<view class="divider"></view>
|
||
|
<uni-easyinput
|
||
|
trim="all"
|
||
|
primaryColor="#29BBE4"
|
||
|
:input-border="false"
|
||
|
v-model="verifyIdentityFormData.code"
|
||
|
:placeholder="$t('form.captcha.placeholder')"
|
||
|
></uni-easyinput>
|
||
|
<view class="sendCodeBtn" @click="handleSendCode">
|
||
|
<text v-if="!sendBtnDisabled">
|
||
|
{{ $t('form.verificationCode.send') }}
|
||
|
</text>
|
||
|
<text v-if="sendBtnLoading">{{ $t('form.verificationCode.sending') }}</text>
|
||
|
<text v-else-if="sendBtnDisabled">{{ sendBtnWaiting }}</text>
|
||
|
</view>
|
||
|
</view>
|
||
|
</uni-forms-item>
|
||
|
</uni-forms>
|
||
|
</view>
|
||
|
<view v-show="active === 1">
|
||
|
<uni-forms ref="changePWDForm" :modelValue="changePWDFormData" :rules="changePWDFormRules">
|
||
|
<uni-forms-item name="password">
|
||
|
<view class="forgotPWDInputWrapper">
|
||
|
<image src="/static/pwd.png" mode="aspectFit" class="icon" style="width: 24px"></image>
|
||
|
<view class="divider" style="margin-left: 16px"></view>
|
||
|
<uni-easyinput
|
||
|
:input-border="false"
|
||
|
type="password"
|
||
|
trim="all"
|
||
|
primaryColor="#29BBE4"
|
||
|
v-model="changePWDFormData.password"
|
||
|
:placeholder="$t('form.newPassword.placeholder')"
|
||
|
@input="passwordInputChange"
|
||
|
></uni-easyinput>
|
||
|
</view>
|
||
|
<view v-if="pwdValidating" class="validateStatusWrapper">
|
||
|
<view v-for="(status, index) in validateStatuses" :key="index" class="validateStatus">
|
||
|
<view class="verificationRes">
|
||
|
<image :src="status.valid ? '/static/right.png' : '/static/error.png'" :class="status.valid ? 'right' : 'error'" mode="aspectFit"></image>
|
||
|
</view>
|
||
|
<view :class="status.valid ? 'rightText' : 'errorText'">{{ status.text }}</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</uni-forms-item>
|
||
|
<uni-forms-item name="confirmPassword">
|
||
|
<view class="forgotPWDInputWrapper">
|
||
|
<image src="/static/confirmPwd.png" mode="aspectFit" class="icon" style="width: 24px"></image>
|
||
|
<view class="divider" style="margin-left: 16px"></view>
|
||
|
<uni-easyinput
|
||
|
:input-border="false"
|
||
|
trim="all"
|
||
|
type="password"
|
||
|
primaryColor="#29BBE4"
|
||
|
v-model="changePWDFormData.confirmPassword"
|
||
|
:placeholder="$t('form.confirmPassword.placeholder')"
|
||
|
@input="confirmPasswordInputChange"
|
||
|
></uni-easyinput>
|
||
|
</view>
|
||
|
<view v-if="confirmPwdValidating" class="validateStatusWrapper">
|
||
|
<view v-for="(status, index) in confirmValidateStatuses" :key="index" class="validateStatus">
|
||
|
<view class="verificationRes">
|
||
|
<image :src="status.valid ? '/static/right.png' : '/static/error.png'" :class="status.valid ? 'right' : 'error'" mode="aspectFit"></image>
|
||
|
</view>
|
||
|
<view :class="status.valid ? 'rightText' : 'errorText'">{{ status.text }}</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</uni-forms-item>
|
||
|
</uni-forms>
|
||
|
</view>
|
||
|
<view v-show="active === 2" class="success">
|
||
|
<image src="/static/success.png" mode="aspectFit" class="successIcon"></image>
|
||
|
<text class="successText">{{ $t('forgotPassword.resetPwdSuccess') }}</text>
|
||
|
</view>
|
||
|
<button class="primaryButton" hover-class="btnHover" type="button" @click="nextStep">
|
||
|
{{ active > 1 ? $t('forgotPassword.backToLogin') : $t('form.next') }}
|
||
|
</button>
|
||
|
</view>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
import { sendCode, checkCode, saveNewPwd } from '@/services/user/forgotPassword';
|
||
|
import { UserLanguage, patterns } from '@/utils/const';
|
||
|
export default {
|
||
|
data() {
|
||
|
return {
|
||
|
active: 0,
|
||
|
stepList: [
|
||
|
{
|
||
|
title: this.$t('forgotPassword.step1')
|
||
|
},
|
||
|
{
|
||
|
title: this.$t('forgotPassword.step2')
|
||
|
},
|
||
|
{
|
||
|
title: this.$t('forgotPassword.step3')
|
||
|
}
|
||
|
],
|
||
|
sendBtnDisabled: false,
|
||
|
sendBtnLoading: false,
|
||
|
sendBtnWaiting: '',
|
||
|
sign: undefined,
|
||
|
authenticationPassed: false,
|
||
|
pwdValidating: false,
|
||
|
confirmPwdValidating: false,
|
||
|
validateStatuses: [
|
||
|
{ valid: false, text: this.$t('form.password.pattern1') },
|
||
|
{ valid: false, text: this.$t('form.password.pattern2') },
|
||
|
{ valid: false, text: this.$t('form.password.pattern3') },
|
||
|
{ valid: false, text: this.$t('form.password.pattern4') }
|
||
|
],
|
||
|
confirmValidateStatuses: [
|
||
|
{ valid: false, text: this.$t('form.password.pattern1') },
|
||
|
{ valid: false, text: this.$t('form.password.pattern2') },
|
||
|
{ valid: false, text: this.$t('form.password.pattern3') },
|
||
|
{ valid: false, text: this.$t('form.password.pattern4') }
|
||
|
],
|
||
|
verifyIdentityFormData: {
|
||
|
email: undefined,
|
||
|
code: undefined
|
||
|
},
|
||
|
verifyIdentityFormRules: {
|
||
|
email: {
|
||
|
rules: [
|
||
|
{
|
||
|
required: true,
|
||
|
errorMessage: this.$t('form.registerEmail.placeholder')
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
code: {
|
||
|
rules: [
|
||
|
{
|
||
|
required: true,
|
||
|
errorMessage: this.$t('form.captcha.required')
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
},
|
||
|
changePWDFormData: {
|
||
|
password: undefined,
|
||
|
confirmPassword: undefined
|
||
|
},
|
||
|
changePWDFormRules: {
|
||
|
password: {
|
||
|
rules: [
|
||
|
{
|
||
|
required: true,
|
||
|
errorMessage: this.$t('form.password.required')
|
||
|
},
|
||
|
{
|
||
|
validateFunction: (rule, value, data, callback) => {
|
||
|
// 异步需要返回 Promise 对象
|
||
|
return new Promise((resolve, reject) => {
|
||
|
setTimeout(() => {
|
||
|
if (this.validateStatuses.every((item) => item.valid)) {
|
||
|
// 通过返回 resolve
|
||
|
resolve();
|
||
|
} else {
|
||
|
// 不通过返回 reject(new Error('错误信息'))
|
||
|
reject(new Error(this.$t('form.password.invalid')));
|
||
|
}
|
||
|
}, 0);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
confirmPassword: {
|
||
|
rules: [
|
||
|
{
|
||
|
required: true,
|
||
|
errorMessage: this.$t('form.confirmPassword.required')
|
||
|
},
|
||
|
{
|
||
|
validateFunction: (rule, value, data, callback) => {
|
||
|
// 异步需要返回 Promise 对象
|
||
|
return new Promise((resolve, reject) => {
|
||
|
setTimeout(() => {
|
||
|
if (this.validateStatuses.every((item) => item.valid)) {
|
||
|
// 通过返回 resolve
|
||
|
resolve();
|
||
|
} else {
|
||
|
// 不通过返回 reject(new Error('错误信息'))
|
||
|
reject(new Error(this.$t('form.password.invalid')));
|
||
|
}
|
||
|
}, 0);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
validateFunction: (rule, value, data, callback) => {
|
||
|
// 异步需要返回 Promise 对象
|
||
|
return new Promise((resolve, reject) => {
|
||
|
setTimeout(() => {
|
||
|
if (this.changePWDFormData.password === this.changePWDFormData.confirmPassword) {
|
||
|
// 通过返回 resolve
|
||
|
resolve();
|
||
|
} else {
|
||
|
// 不通过返回 reject(new Error('错误信息'))
|
||
|
reject(new Error(this.$t('form.confirmPassword.invalid')));
|
||
|
}
|
||
|
}, 0);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
},
|
||
|
computed: {},
|
||
|
onLoad() {},
|
||
|
onShow() {},
|
||
|
methods: {
|
||
|
backToLogin() {
|
||
|
uni.redirectTo({ url: '/pages/login/index' });
|
||
|
},
|
||
|
async handleSendCode() {
|
||
|
this.$refs.verifyIdentityForm.validateField('email').then(async (fields) => {
|
||
|
if (this.sendBtnDisabled) return;
|
||
|
this.sendBtnDisabled = true;
|
||
|
this.sendBtnLoading = true;
|
||
|
const res = await sendCode({ email: fields.email, qcc_language: UserLanguage });
|
||
|
this.sendBtnLoading = false;
|
||
|
this.sendBtnWaiting = 60;
|
||
|
let timer = setInterval(() => {
|
||
|
if (this.sendBtnWaiting > 1) {
|
||
|
this.sendBtnWaiting -= 1;
|
||
|
} else {
|
||
|
this.sendBtnDisabled = false;
|
||
|
clearInterval(timer);
|
||
|
}
|
||
|
}, 1000);
|
||
|
if (res && res.code === 0) {
|
||
|
this.$cusModal.showModal({
|
||
|
type: 'message',
|
||
|
status: 'success',
|
||
|
contentText: this.$t('form.verificationCode.sendSuccess')
|
||
|
});
|
||
|
} else {
|
||
|
this.$cusModal.showModal({
|
||
|
type: 'message',
|
||
|
status: 'warning',
|
||
|
contentText: res.msg ?? this.$t('common.error.sysError')
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
async verifyIdentity() {
|
||
|
let success = false;
|
||
|
await this.$refs.verifyIdentityForm.validate().then(async (fields) => {
|
||
|
const res = await checkCode(fields);
|
||
|
if (res && res.code === 0) {
|
||
|
this.sign = res.data;
|
||
|
success = true;
|
||
|
} else {
|
||
|
this.$cusModal.showModal({
|
||
|
type: 'message',
|
||
|
status: 'warning',
|
||
|
contentText: res.msg ?? this.$t('common.error.sysError')
|
||
|
});
|
||
|
this.sign = undefined;
|
||
|
success = false;
|
||
|
}
|
||
|
});
|
||
|
return success;
|
||
|
},
|
||
|
passwordInputChange(e) {
|
||
|
if (!this.pwdValidating) {
|
||
|
this.pwdValidating = true;
|
||
|
}
|
||
|
this.validateStatuses.forEach((item, index) => {
|
||
|
item.valid = patterns[`passwordPattern${index + 1}`].test(e);
|
||
|
});
|
||
|
},
|
||
|
confirmPasswordInputChange(e) {
|
||
|
if (!this.confirmPwdValidating) {
|
||
|
this.confirmPwdValidating = true;
|
||
|
}
|
||
|
this.confirmValidateStatuses.forEach((item, index) => {
|
||
|
item.valid = patterns[`passwordPattern${index + 1}`].test(e);
|
||
|
});
|
||
|
},
|
||
|
async handleChangePWD() {
|
||
|
let success = false;
|
||
|
await this.$refs.changePWDForm.validate().then(async (fields) => {
|
||
|
delete fields.confirmPassword;
|
||
|
const res = await saveNewPwd({ ...fields, sign: this.sign, email: this.verifyIdentityFormData.email });
|
||
|
if (res && res.code === 0) {
|
||
|
success = true;
|
||
|
} else {
|
||
|
this.$cusModal.showModal({
|
||
|
type: 'message',
|
||
|
status: 'warning',
|
||
|
contentText: res.msg ?? this.$t('common.error.sysError')
|
||
|
});
|
||
|
success = false;
|
||
|
}
|
||
|
});
|
||
|
return success;
|
||
|
},
|
||
|
async nextStep() {
|
||
|
switch (this.active) {
|
||
|
case 0:
|
||
|
const valid = await this.verifyIdentity();
|
||
|
if (valid) {
|
||
|
this.active += 1;
|
||
|
}
|
||
|
break;
|
||
|
case 1:
|
||
|
const ok = await this.handleChangePWD();
|
||
|
if (ok) {
|
||
|
this.active += 1;
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
this.backToLogin();
|
||
|
break;
|
||
|
defaultf;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
@import './index.scss';
|
||
|
.is-input-border {
|
||
|
border: none;
|
||
|
}
|
||
|
</style>
|