保誠-保戶業務員媒合平台
Mila
2024-12-26 078cdb2b41d1dec47e2d981c2d2e618d12beddb4
feat(顧問登入): 串接 otp 發送/驗證 api
修改2個檔案
151 ■■■■■ 已變更過的檔案
PAMapp/pages/consultantLogin/index.vue 110 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/shared/services/login.service.ts 41 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/consultantLogin/index.vue
@@ -27,7 +27,7 @@
          <div class="password-reset" @click="resetPassword">忘記密碼</div>
        </div>
        <div class="position-r mt-10">
          <input :type="checkPasswordType ? 'text' : 'password'"
          <input :type="checkInputType ? 'text' : 'password'"
            v-model="consultantDto.password"
            :disabled="onOTPVerifyStep === 'CAN_RESEND'"
            class="pam-consultant-login__input"
@@ -57,7 +57,7 @@
        </div>
      </div>
      <!--  -->
      <!--  OTP 驗證碼  -->
      <div v-show="onOTPVerifyStep === 'CAN_RESEND'">
        <el-row type="flex" justify="space-between">
            <div class="mdTxt">輸入 OTP 驗證碼</div>
@@ -65,7 +65,6 @@
              {{counterTime(otpCounterSec)}}
            </div>
        </el-row>
        <el-row>
          <input
            class="pam-consultant-login__input mt-10"
@@ -79,7 +78,6 @@
        <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"
@@ -91,18 +89,11 @@
              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'">
       <div class="pam-consultant-login__confirmBlock pam-paragraph">
        <button class="pam-consultant-login__confirm cursor--pointer fix-chrome-click--issue"
          :disabled="!consultantDto.username || !consultantDto.password || !otpCode || !otpCounterSec"
          :disabled="isSentBtnDisabled"
          @click="sendInfo">送出</button>
        </div>
    </div>
@@ -111,8 +102,8 @@
        :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-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
@@ -132,6 +123,7 @@
  import messageBoxService from '~/shared/services/message-box.service';
  import loginService from '~/shared/services/login.service'
  import { AgentInfo } from '~/shared/models/agent-info.model';
import { OtpErrorCode } from '~/shared/models/enum/otpErrorCode';
  const loginStore  = namespace('login.store');
  const roleStorage = namespace('localStorage');
@@ -164,7 +156,7 @@
    otpConfirmVisible = false;
    otpCode = '';
    onOTPVerifyStep: 'APPLY_OTP' | 'CAN_RESEND' = 'APPLY_OTP';
    otpCounterSec = 50;
    otpCounterSec = 300;
    otpResendCounter = 30;
    otpInterval: NodeJS.Timeout | null = null;
    otpIndexKey!: string;
@@ -210,33 +202,14 @@
      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{
      if (this.onOTPVerifyStep === 'APPLY_OTP') {
      this.isAlreadyDone
        ? this.verify()
        : messageBoxService.showErrorMessage('請確認帳號、密碼以及驗證碼是否填寫完畢');
      } else {
        this.login();
    }
    get isAlreadyDone():boolean{
      return !!(this.verificationCode && this.consultantDto.username && this.consultantDto.password);
    }
    resetPassword() {
@@ -246,7 +219,7 @@
    resetOtpSetting() {
      clearInterval(this.otpInterval ?? undefined);
      this.otpResendCounter = 30;
      this.otpCounterSec = 50;
      this.otpCounterSec = 300;
      this.onOTPVerifyStep = 'APPLY_OTP';
      this.otpCode = '';
  }
@@ -265,9 +238,22 @@
      this.otpCode = '';
    }
    get checkPasswordType(): boolean {
    get isAlreadyDone():boolean{
      return !!(this.verificationCode && this.consultantDto.username && this.consultantDto.password);
    }
    get checkInputType(): boolean {
      return this.onOTPVerifyStep === 'CAN_RESEND' ? false : this.isShowPassword;
    }
    get isSentBtnDisabled(): boolean {
      if (this.onOTPVerifyStep === 'APPLY_OTP') {
        return !this.consultantDto.username || !this.consultantDto.password || this.verificationCode.length !== 4;
      } else {
        return !this.consultantDto.username || !this.consultantDto.password || !this.otpCode || !this.otpCounterSec;
      }
    }
    ////////////////////////////////////////////////////////////////////
    private verify():void{
@@ -284,18 +270,15 @@
    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');
        this.applyOtpVerification();
      }).catch((error:AxiosError)=>{
        this.checkHttpErrorStatus(error);
      });
    }
    private checkHttpErrorStatus(error:any):void{
      this.clearValue();
      this.onOTPVerifyStep = 'APPLY_OTP';
      this.regenerateImgOfVerification();
      switch (error.response.status) {
        case 401:
@@ -320,7 +303,21 @@
      this.verificationCode = '';
    }
  private startOtpSetting(type: string) {
    //////////////////// 發送/重發驗證碼
    private applyOtpVerification(): void {
      loginService.sentOtpWithConsultant(this.consultantDto.username).then(otpInfo => {
        if (otpInfo.success) {
          this.otpIndexKey = otpInfo.indexKey;
          this.startOtpSetting();
          this.startOtpCount();
        } else {
          const errorMsg = OtpErrorCode[otpInfo.failCode] ? OtpErrorCode[otpInfo.failCode]:'OTP系統錯誤';
          messageBoxService.showErrorMessage(errorMsg);
        }
      })
    }
    private startOtpSetting() {
    this.onOTPVerifyStep = 'CAN_RESEND';
    this.otpConfirmVisible = true;
  }
@@ -330,15 +327,28 @@
      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)
  }
    private login() {
      const loginVerify = {
        account: this.consultantDto.username,
        indexKey: this.otpIndexKey,
        otpCode: this.otpCode
      }
      loginService.loginVerifyWithConsultant(loginVerify).then(res => {
        this.getLoginConsultantDetail(this.consultantDto.username);
        this.storageIdToken(res.id_token);
        this.storageRole(Role.ADMIN);
        this.storageConsultantId(this.consultantDto.username)
        this.storeUserName();
        this.$router.push('/myAppointmentList/appointmentList');
      })
    }
};
</script>
PAMapp/shared/services/login.service.ts
@@ -102,6 +102,47 @@
    return http.post(`/eService/authenticate/${verificationCode}`, { ...consultantDto, password: encryptedPassword});
  }
  /**
   * 顧問登入 - 發送 otp
   * @param agentNo
   * @returns OtpInfo
   */
  async sentOtpWithConsultant(agentNo: string): Promise<OtpInfo> {
    try {
      const response = await http.post(`/otp/consultant/sendOtp/${agentNo}`);
      // 弱掃Test1: 改為 if (response)
      if (response) {
        return response.data;
      } else {
        throw new Error('http.post returned null-like value.');
      }
    } catch (error) {
      console.error('An error occurred while sending OTP:', error);
      // 可以在此處處理錯誤或回傳預設值
      throw error;
    }
  }
    /**
   * 顧問登入-驗證OTP
   * @param loginVerify 包含驗證相關資訊的物件
   * @returns 回傳驗證成功後的Token
   */
    async loginVerifyWithConsultant(loginVerify: LoginVerify): Promise<LoginSuccessToken> {
      try {
        const response = await http.post('/otp/consultant/verifyOtp', loginVerify);
        if (response !== null) {
          return response.data;
        } else {
          throw new Error('http.post returned null-like value.');
        }
      } catch (error) {
        // 可以在此處處理錯誤或回傳預設值
        console.error('An error occurred while verifying OTP:', error);
        throw error;
      }
    }
  async logout(): Promise<void> {
    return http.post('/logout');
  }