From cfd8c1b9f5acce841d118d951458565d16ba5719 Mon Sep 17 00:00:00 2001 From: 劉鈞霖 <benson@gmail.com> Date: 星期三, 24 十一月 2021 14:59:46 +0800 Subject: [PATCH] Merge branch 'master' of ssh://dev.pollex.com.tw:29418/pcalife/PAM --- PAMapp/pages/login/index.vue | 421 +++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 297 insertions(+), 124 deletions(-) diff --git a/PAMapp/pages/login/index.vue b/PAMapp/pages/login/index.vue index 39bb3ea..0b33aac 100644 --- a/PAMapp/pages/login/index.vue +++ b/PAMapp/pages/login/index.vue @@ -1,104 +1,129 @@ <template> <div class="pam-login-page"> - <div class="text--middle">��</div> + <div class='mb-30'> + <div class="mdTxt">���撘�</div> + <div class="pam-field-title__hint mt-5 mb-10" + >憿批���誑甇廾{connectDevice === 'MOBILE' ? '����Ⅳ' : 'Email'}}�銝餉���垣閰Z蝜急撘�</div> - <div class="pam-paragraph"> - <div class="mdTxt"> - 撽�撘�<small class="pam-field-title__hint pl-10">(憿批��誑�����撘��蝜�)</small> - </div> - <div class="pam-tags"> - <el-row type="flex" class="pt-10"> - <el-button - :class="{ 'active': connectDevice === 'MOBILE'}" - @click="connectDevice = 'MOBILE'">����Ⅳ</el-button> - <el-button - :class="{ 'active': connectDevice === 'EMAIL'}" - @click="connectDevice = 'EMAIL'">Email</el-button> - </el-row> - - </div> - <el-row type="flex" class="pt-10" v-show="connectDevice === 'MOBILE'"> - <input - class="pam-input" - :class="{ - 'is-invalid': !phoneNumber - }" - v-model="phoneNumber" - placeholder="隢撓�����Ⅳ" - > - </el-row> - - <el-row class="pt-10" v-show="connectDevice === 'EMAIL'"> - <input - class="pam-input" - :class="{ - 'is-invalid': !phoneNumber - }" - v-model="email" - placeholder="隢撓� Email ���" - > - </el-row> - - <div class="pt-30" v-show="showPhoneOtpCodeField"> - <el-row type="flex" justify="space-between"> - <div class="mdTxt">頛詨撽�Ⅳ</div> - <div class="otp-count-timer"> - 13:50 - </div> + <div class="pam-tags"> + <el-row type="flex" class="pt-30"> + <el-button + :class="{ 'active': connectDevice === 'MOBILE'}" + @click="connectDevice = 'MOBILE'">����Ⅳ</el-button> + <el-button + :class="{ 'active': connectDevice === 'EMAIL'}" + @click="connectDevice = 'EMAIL'">Email</el-button> </el-row> + </div> - <el-row class="pt-10"> + <div class="pam-inputs mb-10"> + <div class="pt-10" v-show="connectDevice === 'MOBILE'"> + <input + class="pam-input" + :class="{ + 'is-invalid': !phoneValid + }" + v-model="phoneNumber" + placeholder="隢撓�����Ⅳ"> + <div class="error mt-5 mb-5"> + <span v-show="!phoneValid">����Ⅳ�撘�炊</span> + </div> + </div> + + <div class="pt-10" v-show="connectDevice === 'EMAIL'"> <input class="pam-input" :class="{ - 'is-invalid': !otpCode + 'is-invalid': !emailValid }" - v-model="otpCode" - placeholder="隢撓�撽�Ⅳ" + v-model="email" + placeholder="隢撓� Email ���" + > + <div class="error mt-5 mb-5"> + <span v-show="!emailValid">Email�撘�炊</span> + </div> + </div> + </div> + + <!-- mobile 撽�Ⅳ --> + <template v-if="connectDevice === 'MOBILE'"> + <div v-show="showPhoneOtpCodeField"> + <el-row type="flex" justify="space-between"> + <div class="mdTxt">頛詨撽�Ⅳ</div> + <div class="otp-count-timer"> + {{otpCounter}} + </div> + </el-row> + + <el-row class="mb-30"> + <input + class="pam-input mt-10" + :class="{ + 'is-invalid': !otpCode + }" + v-model="otpCode" + placeholder="隢撓�撽�Ⅳ" + > + </el-row> + + <el-row> + <el-button + :disabled="!phoneNumber || otpResendCounter !== 0 || !phoneValid" + @click="resentOtp('MOBILE')" + icon="icon-arrow" > + ��撽�Ⅳ<span class="pam-field-title__hint pl-5">({{ otpResendCounter }})</span> + </el-button> + </el-row> + </div> + + <el-row> + <el-button + v-if="onPhoneVerifyStep === 'APPLY_OTP'" + :disabled="!phoneNumber || !phoneValid" + @click="applyOtpVerification('MOBILE')" + icon="icon-arrow" + > + ����Ⅳ + </el-button> </el-row> - <el-row class="pt-10"> - <button - class="pam-otp-resend-btn" - :class="{'disabled': true}"> - <i class="icon-arrow"></i> - ��撽�Ⅳ({{ otpResendCounter }}) - </button> - </el-row> - </div> + </template> - <div v-show="showEmailVerifyField"> - <el-row class="pt-10"> - <button - class="pam-otp-resend-btn" - :class="{'disabled': onEmailVerifyResendStatus === 'CANNOT_RESEND'}"> - <i class="icon-arrow"></i> - ��撽�Ⅳ({{ emailResendCounter }}) - </button> + <!-- email 撽�Ⅳ --> + <template v-if="connectDevice === 'EMAIL'"> + <el-row v-show="showEmailVerifyField"> + <el-button + :disabled="!email || emailResendCounter !== 0 || !emailValid" + icon="icon-arrow" + @click="resentOtp('EMAIL')" + > + ��撽�Ⅳ<span class="pam-field-title__hint pl-5">({{ emailResendCounter }})</span> + </el-button> + <div class="mt-10 smTxt_bold text--primary">! 隢�����mail敺�</div> </el-row> - </div> + + <el-row v-show="!showEmailVerifyField"> + <el-button + :disabled="!email || !emailValid" + @click="applyOtpVerification('EMAIL')" + icon="icon-arrow" + > + ����Ⅳ + </el-button> + </el-row> + </template> </div> - <el-row type="flex" justify="center" class="pam-login-page__action-bar"> - <div v-if="connectDevice === 'MOBILE'"> - <el-button - type="primary" - v-if="onPhoneVerifyStep === 'APPLY_OTP'" - :disabled="!phoneNumber" - @click="applyOtpVerification"> - ����Ⅳ - </el-button> - <el-button - type="primary" - v-if="connectDevice === 'MOBILE' && onPhoneVerifyStep === 'INPUT_OTP'" - :disabled="!otpCode" - @click="registerDialogVisable = true"> - � - </el-button> - </div> - + <el-row type="flex" justify="center" class="pam-login-page__action-bar mt-30"> + <el-button + type="primary" + v-if="connectDevice === 'MOBILE' && onPhoneVerifyStep === 'INPUT_OTP'" + :disabled="!otpCode || !phoneNumber || !phoneValid" + @click="phoneLogin"> + � + </el-button> </el-row> <el-dialog @@ -213,7 +238,22 @@ </el-dialog> <PopUpFrame class="pam-popUpFrame" - :isOpen.sync="applySuccessConfirmVisable"> + :isOpen.sync="emailOtpConfirmVisable"> + <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 pam-paragraph"> + <div class="text--center"> + <el-button + type="primary" + @click="emailOtpConfirmVisable = false" + >������</el-button> + </div> + </div> + </PopUpFrame> + + <PopUpFrame class="pam-popUpFrame" + :isOpen.sync="registerSuccessConfirmVisable"> <div class="pam-popUp-title text--center"> 甇∟�����������垣閰g�“���誑�����{ connectDevice === 'MOBILE' ? '����Ⅳ' : 'Email'}}���蝜� </div> @@ -227,14 +267,27 @@ </div> </PopUpFrame> - <el-button class="mt-30" @click="fakeLogin">摰X��</el-button> + <PopUpFrame class="pam-popUpFrame" + :isOpen.sync="phoneSuccessConfirmVisable"> + <div class="pam-popUp-title text--center mb-50" + >甇∟�������</div> + <div class="pam-popUp-confirm-bolck pam-paragraph"> + <div class="text--center"> + <el-button + type="primary" + @click="confirmApplySuccess" + >������</el-button> + </div> + </div> + </PopUpFrame> + </div> </template> <script lang="ts"> import { namespace } from 'nuxt-property-decorator'; import { Vue, Component } from 'vue-property-decorator'; -import { login } from '~/assets/ts/api/consultant'; +import { LoginRequest, loginVerify, OtpInfo, register, RegisterInfo, sendOtp } from '~/assets/ts/api/consultant'; import { Role } from '~/assets/ts/models/enum/Role'; const roleStorage = namespace('localStorage'); @@ -249,20 +302,33 @@ phoneNumber = ''; otpCode = ''; onPhoneVerifyStep: 'APPLY_OTP' | 'INPUT_OTP' | 'SUBMIT_OTP' = 'APPLY_OTP'; + otpCounter = '15:00'; otpResendCounter = 30; + otpInterval: any; + phoneOtpInfo!: OtpInfo; email = ''; - onEmailVerifyResendStatus: 'CAN_RESEND' | 'CANNOT_RESEND' = 'CANNOT_RESEND'; + onEmailVerifyResendStatus: 'APPLY_OTP' | 'CAN_RESEND' = 'APPLY_OTP'; emailResendCounter = 30; + emailResendInterval: any; + emailOtpInfo!: OtpInfo; - registerDialogVisable = false; - applySuccessConfirmVisable = false; name = ''; agreeControct = false; isReadContract = false; + phoneSuccessConfirmVisable = false; + emailOtpConfirmVisable = false; + + registerDialogVisable = false; + registerSuccessConfirmVisable = false; + detectContructReadStatus(event: any): void { - this.isReadContract = Math.round(event.target.scrollTop) === (event.target.scrollHeight - event.target.clientHeight); + const scrollTop = Math.round(event.target.scrollTop); + const height = event.target.scrollHeight - event.target.clientHeight; + if (Math.floor(scrollTop/10) === (Math.floor(height/10))) { + this.isReadContract = true; + } }; get showPhoneOtpCodeField(): boolean { @@ -270,34 +336,141 @@ }; get showEmailVerifyField(): boolean { - return this.connectDevice === 'EMAIL'; + return this.connectDevice === 'EMAIL' && this.onEmailVerifyResendStatus !== 'APPLY_OTP'; }; - applyOtpVerification(): void { - this.onPhoneVerifyStep = 'INPUT_OTP'; + get phoneValid() { + const rule = /^09[0-9]{8}$/; + return this.phoneNumber ? rule.test(this.phoneNumber) : true; + } + + get emailValid() { + const rule = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; + return this.email ? rule.test(this.email) : true; + } + + applyOtpVerification(type: string): void { + const isMobile = this.connectDevice === 'MOBILE'; + const loginInfo: LoginRequest = { + loginType: isMobile ? 'SMS' : 'EMAIL', + account: isMobile ? this.phoneNumber : this.email, + } + sendOtp(loginInfo).then(otpInfo => { + if (otpInfo.success) { + this.startOtpCount(type, otpInfo); + } + }); }; + + resentOtp(type: string) { + if (type === 'MOBILE') { + clearInterval(this.otpInterval); + this.otpResendCounter = 30; + this.otpCounter = '15:00'; + this.startPhoneCounter(); + } else { + this.emailResendCounter = 30; + this.startEmailCounter(); + this.emailOtpConfirmVisable = true; + } + } + + private startOtpCount(type: string, otpInfo) { + type === 'MOBILE' ? this.phoneOtpInfo = otpInfo : this.emailOtpInfo = otpInfo; + if (type === 'MOBILE') { + this.onPhoneVerifyStep = 'INPUT_OTP'; + this.startPhoneCounter(); + } else { + this.onEmailVerifyResendStatus = 'CAN_RESEND'; + this.startEmailCounter(); + this.emailOtpConfirmVisable = true; + } + } + + private startEmailCounter() { + this.emailResendInterval = setInterval(() => { + this.emailResendCounter -= 1; + if (this.emailResendCounter === 0) { + clearInterval(this.emailResendInterval) + } + }, 1000) + } + + private startPhoneCounter() { + const minCount = this.otpCounter.split(':'); + let secCount = (+minCount[0] * 60) + (+minCount[1]); + let min = 0; + let sec = 0; + this.otpInterval = setInterval(() => { + secCount -= 1; + min = Math.floor(secCount/60); + sec = Math.floor(secCount%60); + this.otpCounter = `${min < 10 ? '0' + min : min}:${sec < 10 ? '0' + sec : sec}`; + + if (this.otpResendCounter !== 0) { + this.otpResendCounter -= 1; + } + + if (secCount === 0) { + clearInterval(this.otpInterval) + } + }, 1000) + } + + private setRegisterInfo(): RegisterInfo { + return this.connectDevice === 'MOBILE' + ? { + phone: this.phoneNumber, + indexKey: this.phoneOtpInfo.indexKey, + otpCode: this.otpCode, + name: this.name, + contactType: 'SMS' + } + : { + email: this.email, + indexKey: this.emailOtpInfo.indexKey, + otpCode: this.otpCode, + name: this.name, + contactType: 'EMAIL' + } + } applyAccount(): void { - this.applySuccessConfirmVisable = true; - console.log('apply new account!') + const registerInfo = this.setRegisterInfo(); + register(registerInfo).then(res => { + this.storageIdToken(res.data.id_token); + this.storageRole(Role.USER); + this.registerSuccessConfirmVisable = true; + }) }; confirmApplySuccess(): void { - this.applySuccessConfirmVisable = false + this.phoneSuccessConfirmVisable = false; + this.registerSuccessConfirmVisable = false; + this.$router.go(-1); } - // TODO: ��TP隤����� ���蝙� - fakeLogin() { - const user = { - username: 'user', - password: 'user', - } - login(user).then((res) => { - this.storageIdToken(res.data.id_token); - this.storageRole(Role.USER); - this.$router.go(-1); - }) - }; + phoneLogin() { + const login = { + account: this.phoneNumber, + indexKey: this.phoneOtpInfo.indexKey, + otpCode: this.otpCode + } + loginVerify(login).then(res => { + this.storageIdToken(res.data.id_token); + this.storageRole(Role.USER); + this.phoneSuccessConfirmVisable = true; + }).catch(error => { + if (error.response.status === 401) { + this.registerDialogVisable = true; + } + }) + } + + destroyed() { + clearInterval(this.otpInterval); + clearInterval(this.emailResendInterval); + } } </script> @@ -319,8 +492,7 @@ width: calc(100% - 36px); border-radius: 10px !important; padding: 12px 18px !important; - border-width: 1px; - outline: none; + border:1px solid #CCCCCC; @extend .text--middle; &::placeholder { color: $PRUDENTIAL_GREY; @@ -328,19 +500,6 @@ &.is-invalid { border: 1px solid $PRIMARY_RED !important; border-radius: 20px; - } -} - -.pam-otp-resend-btn { - background: transparent; - border: none; - color: $PRIMARY_RED; - font-weight: bold; - i { - margin-right: 10px; - } - &.disabled { - color: $LIGHT_GREY; } } @@ -396,4 +555,18 @@ } } +.pam-field-title__hint { + @extend .smTxt_bold; + color: #68737A; +} + +.error { + @extend .smTxt_bold; + @extend .text--primary; + height: 16px; +} + +.pam-popUp-title { + line-height: 24px; +} </style> -- Gitblit v1.8.0