<template>
|
<div>
|
<div class="pam-consultant-login">
|
<div class="pam-consultant-login__header pam-paragraph">顧問登入</div>
|
<div class="pam-paragraph">
|
<div class="pam-consultant-login__title">帳號</div>
|
<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 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>
|
記住
|
</div>
|
</div>
|
</div>
|
<div class="pam-paragraph">
|
<div class="password-Txt">
|
<div class="pam-consultant-login__title ">密碼</div>
|
<div class="password-reset" @click="resetPassword">忘記密碼</div>
|
</div>
|
<div class="position-r mt-10">
|
<input :type="checkPasswordType ? 'text' : 'password'"
|
v-model="consultantDto.password"
|
:disabled="onOTPVerifyStep === 'CAN_RESEND'"
|
class="pam-consultant-login__input"
|
placeholder="輸入eService密碼">
|
<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" 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"
|
@click="regenerateImgOfVerification">重新產生</div>
|
</div>
|
<div class="pam-consultant-login__verifyBlock mt-10">
|
<div class="w-55">
|
<input type="text"
|
v-model="verificationCode"
|
maxlength="4"
|
class="pam-consultant-login__input">
|
</div>
|
<div class="pam-consultant-login__verifyImg">
|
<img :src="imgSrc" alt="驗證碼">
|
</div>
|
</div>
|
</div>
|
|
<!-- -->
|
<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, 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';
|
|
const loginStore = namespace('login.store');
|
const roleStorage = namespace('localStorage');
|
|
@Component({
|
layout: 'home'
|
})
|
export default class ConsultantLogin extends Vue {
|
@roleStorage.Mutation
|
storageIdToken!: (token: string) => void;
|
|
@roleStorage.Mutation
|
storageRole!: (role: string) => void;
|
|
@roleStorage.Mutation
|
storageConsultantId!:(id:string) => void;
|
|
@loginStore.Action
|
getLoginConsultantDetail!: (agentNo: string) => Promise<AgentInfo>;
|
|
consultantDto = {
|
password: '',
|
username: '',
|
};
|
imgSrc = '';
|
isRememberUserName = false;
|
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')
|
if (username) {
|
this.consultantDto.username = username;
|
this.isRememberUserName = true;
|
}
|
}
|
|
////////////////////////////////////////////////////////////////////
|
|
|
public regenerateImgOfVerification(): void {
|
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{
|
this.isAlreadyDone
|
? this.verify()
|
: messageBoxService.showErrorMessage('請確認帳號、密碼以及驗證碼是否填寫完畢');
|
}
|
|
get isAlreadyDone():boolean{
|
return !!(this.verificationCode && this.consultantDto.username && this.consultantDto.password);
|
}
|
|
resetPassword() {
|
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{
|
loginService.getVerificationStatus(this.verificationCode).then( verifySuccess => {
|
if(verifySuccess.data){
|
this.loginWithConsultant();
|
}else{
|
this.clearValue();
|
this.regenerateImgOfVerification();
|
messageBoxService.showErrorMessage('驗證碼輸入錯誤');
|
}
|
});
|
}
|
|
private loginWithConsultant(): void {
|
loginService.logInToConsultant(this.consultantDto, this.verificationCode).then(res => {
|
this.getLoginConsultantDetail(this.consultantDto.username);
|
this.storageIdToken(res.data.id_token);
|
this.storageRole(Role.ADMIN);
|
this.storageConsultantId(this.consultantDto.username)
|
this.storeUserName();
|
this.$router.push('/myAppointmentList/appointmentList');
|
}).catch((error:AxiosError)=>{
|
this.checkHttpErrorStatus(error);
|
});
|
}
|
private checkHttpErrorStatus(error:any):void{
|
this.clearValue();
|
this.regenerateImgOfVerification();
|
switch (error.response.status) {
|
case 401:
|
const errorMsg = error.response.data.detail;
|
messageBoxService.showErrorMessage(errorMsg);
|
break;
|
|
default:
|
const defaultErrorMsg = error.response.data.title
|
messageBoxService.showErrorMessage('',defaultErrorMsg);
|
break;
|
}
|
}
|
|
private storeUserName(): void {
|
localStorage.setItem('consultantUserName', this.isRememberUserName ? this.consultantDto.username : '');
|
};
|
|
private clearValue():void{
|
if (!this.isRememberUserName) this.consultantDto.username='';
|
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>
|
.mt-20 {
|
margin-top: 20px;
|
}
|
|
.mt-25 {
|
margin-top: 25px;
|
}
|
|
.w-55 {
|
width: 55% !important;
|
}
|
|
.position-r {
|
position: relative;
|
}
|
.password-Txt {
|
display: flex;
|
justify-content: space-between;
|
align-items: flex-end;
|
}
|
.password-reset {
|
font-size: 16px ;
|
cursor: pointer;
|
}
|
|
.pam-popUp-title {
|
font-size: 20px;
|
line-height: 27px;
|
}
|
|
.pam-consultant-login {
|
margin: auto;
|
width: 336px;
|
font-size: 20px;
|
color: $PRIMARY_BLACK;
|
|
&__header {
|
text-align: center;
|
font-size: 24px;
|
font-weight: bold;
|
letter-spacing: 1.2;
|
color: $PRIMARY_BLACK;
|
}
|
|
&__title {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
&__input {
|
width: 100%;
|
outline: 0;
|
border: 1px solid #CCCCCC;
|
border-radius: 10px;
|
font-size: 20px;
|
height: 50px;
|
padding: 10px 90px 10px 15px;
|
overflow: auto;
|
box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
|
&Icon {
|
position: absolute;
|
display: flex;
|
align-items: center;
|
top: 15px;
|
right: 15px;
|
}
|
}
|
|
&__forgot-password {
|
color: $PRIMARY_RED;
|
text-decoration: none;
|
font-size: 16px;
|
}
|
|
&__verifyBlock {
|
display: flex;
|
justify-content: space-between;
|
}
|
|
&__verifyImg {
|
width: 126px;
|
height: 50px;
|
border:1px #cccccc solid;
|
img {
|
width: 100%;
|
height: 100%;
|
}
|
}
|
|
&__confirmBlock {
|
display: flex;
|
justify-content: center;
|
}
|
|
&__confirm {
|
color: $PRIMARY_WHITE;
|
width: 80px;
|
height: 50px;
|
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>
|