From 68a01779c9df1c8b8d68a642204e80c1b3ff896e Mon Sep 17 00:00:00 2001 From: Mila <Mila@pollex.com.tw> Date: 星期三, 25 十二月 2024 15:05:40 +0800 Subject: [PATCH] feat(顧問登入): 顧問登入頁增加otp驗證流程 --- PAMapp/pages/consultantLogin/index.vue | 196 +++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 183 insertions(+), 13 deletions(-) diff --git a/PAMapp/pages/consultantLogin/index.vue b/PAMapp/pages/consultantLogin/index.vue index 421f532..34f930e 100644 --- a/PAMapp/pages/consultantLogin/index.vue +++ b/PAMapp/pages/consultantLogin/index.vue @@ -7,9 +7,14 @@ <div class="position-r mt-10"> <input type="text" v-model="consultantDto.username" + :disabled="onOTPVerifyStep === 'CAN_RESEND'" class="pam-consultant-login__input" placeholder="頛詨eService撣唾��"> - <div class="pam-consultant-login__inputIcon text--primary cursor--pointer fix-chrome-click--issue" + <div v-if="onOTPVerifyStep === 'CAN_RESEND'" class="pam-consultant-login__inputIcon cursor--pointer fix-chrome-click--issue" + @click="deleteOtpInfo()"> + <i class="icon-close"></i> + </div> + <div v-if="onOTPVerifyStep !== 'CAN_RESEND'" class="pam-consultant-login__inputIcon text--primary cursor--pointer fix-chrome-click--issue" @click="isRememberChange"> <i :class="[isRememberUserName ? 'icon-checkbox-1' : 'icon-checkbox','pr-5']"></i> 閮�� @@ -22,17 +27,18 @@ <div class="password-reset" @click="resetPassword">敹��Ⅳ</div> </div> <div class="position-r mt-10"> - <input :type="[ isShowPassword ? 'text' : 'password']" + <input :type="checkPasswordType ? 'text' : 'password'" v-model="consultantDto.password" + :disabled="onOTPVerifyStep === 'CAN_RESEND'" class="pam-consultant-login__input" placeholder="頛詨eService撖Ⅳ"> - <div class="pam-consultant-login__inputIcon cursor--pointer fix-chrome-click--issue" + <div v-if="onOTPVerifyStep !== 'CAN_RESEND'" class="pam-consultant-login__inputIcon cursor--pointer fix-chrome-click--issue" @click="isShowPassword = !isShowPassword"> <i :class="[isShowPassword ? 'icon-eye':'icon-eye-1 fs-25', 'text--primary']"></i> </div> </div> </div> - <div class="pam-paragraph"> + <div class="pam-paragraph" v-if="onOTPVerifyStep === 'APPLY_OTP'"> <div class="pam-consultant-login__title"> <div>撽�Ⅳ <span class="text--dark-blue fs-16">(����之撠神)</span></div> <div class="text--primary fs-16 cursor--pointer fix-chrome-click--issue" @@ -50,21 +56,82 @@ </div> </div> </div> - <div class="pam-consultant-login__confirmBlock pam-paragraph"> - <button class="pam-consultant-login__confirm cursor--pointer fix-chrome-click--issue" - @click="sendInfo">�</button> + + <!-- --> + <div v-show="onOTPVerifyStep === 'CAN_RESEND'"> + <el-row type="flex" justify="space-between"> + <div class="mdTxt">頛詨 OTP 撽�Ⅳ</div> + <div class="otp-count-timer"> + {{counterTime(otpCounterSec)}} + </div> + </el-row> + + <el-row> + <input + class="pam-consultant-login__input mt-10" + :class="{ + 'is-invalid': !otpCode + }" + v-model="otpCode" + placeholder="隢撓� OTP 撽�Ⅳ" + > + </el-row> + <div class="error mt-5 mb-10"> + <span v-show="otpCounterSec === 0">OTP 撽�Ⅳ撌脤����� OTP 撽�Ⅳ</span> + </div> + + <el-row> + <el-button + :disabled="!consultantDto.password || otpResendCounter !== 0 || !consultantDto.username" + @click="resetOtpSetting()" + icon="icon-arrow" + > + �� OTP 撽�Ⅳ<span + class="pam-field-title__hint pl-5" + v-if="otpResendCounter !== 0" + >({{ otpResendCounter }})</span> + </el-button> + + </el-row> </div> + <div class="pam-paragraph"> + <el-button v-if="onOTPVerifyStep === 'APPLY_OTP'" icon="icon-arrow" + :disabled="!consultantDto.username || !consultantDto.password || verificationCode.length !== 4" + @click="applyOtpVerification"> + ��� OTP 撽�Ⅳ</el-button> + </div> + <div class="pam-consultant-login__confirmBlock pam-paragraph" v-if="onOTPVerifyStep === 'CAN_RESEND'"> + <button class="pam-consultant-login__confirm cursor--pointer fix-chrome-click--issue" + :disabled="!consultantDto.username || !consultantDto.password || !otpCode || !otpCounterSec" + @click="sendInfo">�</button> + </div> </div> + + <PopUpFrame class="pam-popUpFrame" + :isOpen.sync="otpConfirmVisible" + > + <div class="pam-popUp-title text--center">撌脣������</div> + <div class="pam-popUp-title text--center">{{'email'}}</div> + <div class="pam-popUp-title text--center">隢��摮隞嗡蒂摰������</div> + <div class="pam-popUp-confirm-bolck mt-30"> + <div class="text--center"> + <el-button + type="primary" + @click="otpConfirmVisible = false" + >������</el-button> + </div> + </div> + </PopUpFrame> </div> </template> <script lang="ts"> - import { Vue, Component , namespace } from 'nuxt-property-decorator'; + import { Vue, Component , namespace, Watch } from 'nuxt-property-decorator'; import { AxiosError } from 'axios'; import { Role } from '~/shared/models/enum/Role'; import messageBoxService from '~/shared/services/message-box.service'; import loginService from '~/shared/services/login.service' -import { AgentInfo } from '~/shared/models/agent-info.model'; + import { AgentInfo } from '~/shared/models/agent-info.model'; const loginStore = namespace('login.store'); const roleStorage = namespace('localStorage'); @@ -94,12 +161,31 @@ isShowPassword = false; verificationCode=''; + otpConfirmVisible = false; + otpCode = ''; + onOTPVerifyStep: 'APPLY_OTP' | 'CAN_RESEND' = 'APPLY_OTP'; + otpCounterSec = 50; + otpResendCounter = 30; + otpInterval: NodeJS.Timeout | null = null; + otpIndexKey!: string; + + @Watch('onOTPVerifyStep') + onOTPVerifyStepChange() { + if (this.onOTPVerifyStep === 'APPLY_OTP') { + this.regenerateImgOfVerification(); + } + } + //////////////////////////////////////////////////////////////////// mounted() { this.getInitUserName(); this.regenerateImgOfVerification(); }; + + destroyed() { + clearInterval(this.otpInterval ?? undefined); + } private getInitUserName(): void { const username = localStorage.getItem('consultantUserName') @@ -113,14 +199,34 @@ public regenerateImgOfVerification(): void { - loginService.getImgOfVerification().then( imgOfBase64 => - this.imgSrc = imgOfBase64 - ); + loginService.getImgOfVerification().then( imgOfBase64 =>{ + this.imgSrc = imgOfBase64; + this.verificationCode = ''; + }); }; public isRememberChange():void{ this.isRememberUserName = !this.isRememberUserName; this.storeUserName(); + } + + public applyOtpVerification(type: string): void { + // TODO: sendOTP + const otpInfo = { + indexKey: "string", /** ��撣嗅otp隤��� */ + success: true, /** Otp�������� */ + failCode: "string", + failReason: "string", + } + if (otpInfo.success) { + // this.storageOtpTime(type, otpInfo); + this.startOtpSetting(type); + this.startOtpCount(); + } else { + // TODO: otp error + // const errorMsg = OtpErrorCode[otpInfo.failCode] ? OtpErrorCode[otpInfo.failCode]:'OTP蝟餌絞�隤�'; + // messageBoxService.showErrorMessage(errorMsg); + } } public sendInfo():void{ @@ -137,7 +243,31 @@ window.open(process.env.CONSULTANT_FORGET_PASSWORD_URL); } + resetOtpSetting() { + clearInterval(this.otpInterval ?? undefined); + this.otpResendCounter = 30; + this.otpCounterSec = 50; + this.onOTPVerifyStep = 'APPLY_OTP'; + this.otpCode = ''; + } + counterTime(counterSec) { + let min = Math.floor(counterSec / 60); + let sec = Math.floor(counterSec % 60); + return `${min < 10 ? '0' + min : min}:${sec < 10 ? '0' + sec : sec}`; + } + + deleteOtpInfo() { + this.resetOtpSetting(); + this.onOTPVerifyStep = 'APPLY_OTP'; + this.consultantDto.password = ''; + this.consultantDto.username = ''; + this.otpCode = ''; + } + + get checkPasswordType(): boolean { + return this.onOTPVerifyStep === 'CAN_RESEND' ? false : this.isShowPassword; + } //////////////////////////////////////////////////////////////////// private verify():void{ @@ -189,8 +319,27 @@ this.consultantDto.password = ''; this.verificationCode = ''; } - }; + private startOtpSetting(type: string) { + this.onOTPVerifyStep = 'CAN_RESEND'; + this.otpConfirmVisible = true; + } + + private startOtpCount() { + this.otpInterval = setInterval(() => { + this.otpCounterSec -= 1; + if (this.otpResendCounter !== 0) { + this.otpResendCounter -= 1; + if (this.otpResendCounter === 0) { + // this.regenerateImgOfVerification(); + } + } + if (this.otpCounterSec === 0 && this.otpInterval) { + clearInterval(this.otpInterval); + } + }, 1000) + } +}; </script> <style lang="scss" scoped> @@ -217,7 +366,11 @@ .password-reset { font-size: 16px ; cursor: pointer; + } + .pam-popUp-title { + font-size: 20px; + line-height: 27px; } .pam-consultant-login { @@ -295,7 +448,24 @@ border-radius: 30px; border: 1px solid $LIGHT_GREY; background-color: $PRIMARY_RED; + font-weight: 700; + + &:disabled { + color: $PRIMARY_WHITE; + background-color: $MID_GREY; + border-color: $MID_GREY; + } } } + .pam-field-title__hint { + @extend .smTxt_bold; + color: #68737A; + } + + .error { + @extend .smTxt_bold; + @extend .text--primary; + height: 16px; + } </style> -- Gitblit v1.8.0