保誠-保戶業務員媒合平台
jack
2023-09-19 509d17e9885c46af9331666a43eb8766ec72c295
PAMapp/pages/questionnaire/_agentNo.vue
@@ -1,12 +1,12 @@
<template>
  <div class="ques-page--reset">
  <div class="ques-page--reset" v-if="isUserLogin">
    <div class="ques-header">
        <div class="ques-header__mob-banner"></div>
        <div
          class="ques-header__info"
          v-if="myRequest.contactType==='phone'">
          <div class="text--middle">
              <div class="mdTxt">你指定的聯繫方式</div>
              <div class="mdTxt">您指定的聯繫方式</div>
              <div class="mt-10">
                  <span>手機:</span>
                  <span>{{myRequest.phone}}</span>
@@ -16,32 +16,36 @@
              <div class="datepicker required">
                  <span class="mdTxt">手機連絡的方便時間</span>
                  <PhoneContactTimePicker
                  :scheduleList.sync="myRequest.hopeContactTime"/>
                  :scheduleList="myRequest.hopeContactTime"/>
              </div>
          </div>
          <div class="mt-30">
              <div class="mdTxt">其他備用聯繫方式</div>
              <div class="ques-header__input-block">
                  <span>Email:</span>
                  <input class="ques-header__input"
                  <input class="ques-header__input  break"
                    :class="{ 'is-invalid': !emailValid}"
                    placeholder="請輸入"
                    v-model="myRequest.email">
              </div>
              <div class="error mt-5 mb-5" style="margin-left:65px">
                  <span v-show="!emailValid">Email格式有誤</span>
              </div>
          </div>
        </div>
        <div class="ques-header__info" v-else>
          <div class="text--middle">
              <div class="mdTxt">你指定的聯繫方式</div>
              <div class="mdTxt">您指定的聯繫方式</div>
              <div class="mt-10 ques-header__input-block">
                  <span>Email:</span>
                  <span>{{myRequest.email}}</span>
                  <span class=" break">{{myRequest.email}}</span>
              </div>
          </div>
          <div class="mt-30">
              <div class="mdTxt">其他備用聯繫方式</div>
              <div class="ques-header__input-block">
                  <span>手機:</span>
                  <input class="ques-header__input"
                  <span>手機 : </span>
                  <input class="ques-header__input ml-4"
                    :class="{'is-invalid': !phoneValid}"
                    placeholder="請輸入"
                    maxlength="10"
@@ -55,7 +59,7 @@
              <div class="datepicker">
                  <span class="mdTxt">手機連絡的方便時間</span>
                  <PhoneContactTimePicker
                  :scheduleList.sync="myRequest.hopeContactTime"/>
                  :scheduleList="myRequest.hopeContactTime"/>
              </div>
          </div>
        </div>
@@ -63,7 +67,7 @@
    <div class="ques-container">
      <div class="pam-paragraph">
        <div class="mdTxt">
            想要詢問的問題
            想了解的領域
            <span class="hint text--bold">
                (可複選)
            </span>
@@ -74,13 +78,19 @@
          :options="requirementOptions" />
      </div>
      <div class="pam-paragraph">
        <div class="mdTxt">諮詢方式</div>
        <SingleSelectBtn class="mt-10"
           :singleSelected.sync="myRequest.consultationMethod"
           :options="consultationMethodOptions" />
      </div>
      <div class="pam-paragraph">
        <div class="mdTxt">您的性別</div>
        <SingleSelectBtn class="mt-10"
          :singleSelected.sync="myRequest.gender"
          :options="genderOptions" />
      </div>
      <div class="pam-paragraph">
        <div class="mdTxt">年齡</div>
        <div class="mdTxt">您的年齡</div>
        <SingleSelectBtn class="mt-10"
          :singleSelected.sync="myRequest.age"
          :options="ageRangeOptions" />
@@ -92,14 +102,14 @@
        <el-button type="primary"
          :disabled="isDisabledSubmitBtn"
          @click.native="sentDemand">
          送出
          {{isEditBtn ? '更新' : '送出'}}
        </el-button>
      </div>
    </div>
    <PopUpFrame :isOpen.sync="showDrawer" :drawerSize=" '95%' ">
    <PopUpFrame :isOpen.sync="showDrawer">
      <div class="qaTextTitle mdTxt">
        <strong>想要詢問的問題</strong>
        <strong>想了解的領域</strong>
      </div>
      <div class="qa-dialog">
          <div v-for="(qaText,index) in quesAboutList" :key="index" >
@@ -115,28 +125,77 @@
    </PopUpFrame>
    <PopUpFrame :isOpen.sync="sendReserve" @update:isOpen="closeReservePopUp">
        <div class="text--middle  mt-30 sendReserve-txt">預約成功!您預約的保險顧問會</div>
        <div class="text--middle  sendReserve-txt">儘速與您聯絡!</div>
        <div class="mdTxt mt-30 sendReserve-txt">預約成功!</div>
        <div class="mdTxt sendReserve-txt mb-30">您預約的顧問會儘速與您聯絡!</div>
        <div class="pam-app-review mb-10">
          <div class="mdTxt mb-10">對於
                <span class="mdTxt text--primary text--bold ">服務媒合</span>
                平台的整體服務,
            </div>
          <div class="mdTxt">您給予幾顆星評價?</div>
        </div>
        <el-rate v-model="score" class="pam-satisfaction-rate fix-chrome-click--issue"></el-rate>
        <div class="text--center mdTxt">
          <el-button @click="closeReservePopUp">略過</el-button>
          <el-button type="primary"
            @click="closeReservePopUp">
            我知道了
            @click="reviewPlatform">
            送出
          </el-button>
        </div>
    </PopUpFrame>
    <PopUpFrame :isOpen.sync="isEditPopup">
      <div class="text--middle text--center mb-10">已於
        <span class="bold">{{appointmentTime | formatDate}}</span>
      進行預約,</div>
      <div class="text--middle text--center mb-30">是否繼續編輯?</div>
      <div class="text--center mdTxt">
        <el-button @click="$router.go(-1)">返回</el-button>
        <el-button @click="isEditPopup = false" type="primary">編輯</el-button>
      </div>
    </PopUpFrame>
  </div>
</template>
<script lang="ts">
  import { Vue, Component } from 'vue-property-decorator';
  import { addFavoriteConsultant, appointmentDemand, AppointmentParams, AppointmentRequests ,RegisterInfo } from '~/assets/ts/api/consultant';
  import { getRequestsFromStorage, setRequestsToStorage } from '~/assets/ts/storageRequests';
  import { Gender } from '~/assets/ts/models/enum/Gender';
  import { ContactType } from '~/assets/ts/models/enum/ContactType';
  import _ from 'lodash';
import { Vue, Component, State, Action, Watch, namespace } from 'nuxt-property-decorator';
import { getRequestsFromStorage, removeRequestQuestionFromStorage, setRequestsToStorage } from '~/shared/storageRequests';
import _ from 'lodash';
import accountSettingService from '~/shared/services/account-setting.service';
import appointmentService from '~/shared/services/appointment.service';
import authService from '~/shared/services/auth.service';
import queryConsultantService from '~/shared/services/query-consultant.service';
import reviewsService from '~/shared/services/reviews.service';
import { Consultant } from '~/shared/models/consultant.model';
import { ContactType } from '~/shared/models/enum/ContactType';
import { Gender } from '~/shared/models/enum/Gender';
import { RegisterInfo } from '~/shared/models/registerInfo';
import { AppointmentParams, AppointmentRequests } from '~/shared/models/appointment.model';
import { UserSetting } from '~/shared/models/account.model';
import { SatisfactionType } from '~/shared/models/enum/satisfaction-type';
import { UserReviewParams } from '~/shared/models/reviews.model';
  const roleStorage = namespace('localStorage');
  @Component
  export default class Questionnaire extends Vue {
    @State('myConsultantList')
    myConsultantList!: Consultant[];
    @Action
    storeConsultantList!: () => Promise<number>;
    @roleStorage.Getter
    isUserLogin!:boolean;
    @roleStorage.State
    recommendConsultantItem!:string;
    @roleStorage.Mutation
    storageUserInfo!: (params: RegisterInfo) => void;
    score = 0;
    genderOptions=[
      {
        title:'男性',
@@ -145,6 +204,17 @@
      {
        title:'女性',
        label:Gender.FEMALE,
      }
    ];
    consultationMethodOptions = [
      {
        title: '線上',
        label: 'online'
      },
      {
        title: '線下',
        label: 'offline'
      }
    ];
@@ -170,8 +240,8 @@
        label:'保單健檢/規劃',
      },
      {
        title:'分紅保單相關',
        label:'分紅保單相關',
        title:'分紅保單',
        label:'分紅保單',
      },
    ];
@@ -191,10 +261,6 @@
      {
        title:'41-50 歲',
        label:'41-50'
      },
      {
        title:'46-55 歲',
        label:'46-55',
      },
      {
        title:'51-60 歲',
@@ -233,30 +299,62 @@
                  },
                  {
                      title:'分紅保單',
                      content:'分紅保單 分紅保單是兼具「分攤風險」與「紅利共享」特色的保單,具有一定穩定度,讓你可以同時享有壽險保障及紅利!'
                      content:'分紅保單是兼具「分攤風險」與「紅利共享」特色的保單,具有一定穩定度,讓您可以同時享有壽險保障及紅利!'
                  }
    ];
    myRequest: AppointmentRequests = {
      phone: this.userInfo?.phone ? this.userInfo.phone :'',
      email: this.userInfo?.email ? this.userInfo.email :'',
      contactType: _.isEqual(this.userInfo.contactType,ContactType.SMS) ? ContactType.PHONE : ContactType.EMAIL  ,
      gender: '',
      age: '',
      job: '',
      requirement: [],
      name           : '',
      phone          : '',
      email          : '',
      contactType    : _.isEqual(this.userInfo?.contactType,ContactType.SMS) ? ContactType.PHONE: ContactType.EMAIL,
      gender         : '',
      age            : '',
      job            : '',
      requirement    : [],
      hopeContactTime: [{
        selectWeekOptions: [],
        selectWeekOptions : [],
        selectTimesOptions: [],
      }],
      agentNo: '',
      consultationMethod: '',
    };
    showDrawer= false;
    sendReserve = false;
    isEditPopup = false;
    isEditBtn = false;
    mounted() {
    appointmentId = 0;
    appointmentTime = '';
    ////////////////////////////////////////////////////////////////////////////
    beforeRouteEnter(to: any, from: any, next: any) {
      next(vm => {
        const isUserLogin = authService.isUserLogin();
        if (from.name === 'login' && !isUserLogin) {
          vm.$router.go(-1);
          return;
        }
        if (!isUserLogin) {
          vm.$router.push('/login');
        }
      })
    }
    mounted(): void {
      if (authService.isUserLogin()) {
        this.storeConsultantList();
      };
      this.setMyRequest();
    }
    private setMyRequest(): void {
      const storageMyRequest = getRequestsFromStorage();
      const storageMyRequirement = this.recommendConsultantItem ? JSON.parse(this.recommendConsultantItem).requirements:[];
      if (storageMyRequest) {
        this.myRequest = {
          ...storageMyRequest,
@@ -268,32 +366,137 @@
                              }],
        };
      }
      if (storageMyRequirement) {
        this.myRequest = {
          ...this.myRequest,
          requirement: storageMyRequirement
        }
        removeRequestQuestionFromStorage();
      }
      if (authService.isUserLogin()) {
        accountSettingService.getUserAccountSetting().then((contactTypeDetail) => {
              this.myRequest = {
                ...this.myRequest,
                ...contactTypeDetail
              }
        })
      }
    }
    get phoneValid(): boolean {
      const rule = /^09[0-9]{8}$/;
      return this.myRequest.phone
            ? rule.test(this.myRequest.phone) && _.isEqual(this.myRequest.phone.length,10)
            : true;
    ////////////////////////////////////////////////////////////////////////////
    @Watch('myConsultantList')
    onMyConsultantListChange() {
      if (authService.isUserLogin() && this.myConsultantList.length > 0) {
          const editAppointment = this.getLatestReserved(this.$route.params.agentNo);
          if (editAppointment && editAppointment.agentNo) {
            this.myRequest = JSON.parse(JSON.stringify(editAppointment));
            if (!this.$route.query || this.$route.query.edit !== 'true') {
              this.isEditPopup = true;
            }
            this.isEditBtn = true;
          }
      }
    }
    get userInfo(): RegisterInfo {
      const initUserInfo = JSON.parse(localStorage.getItem('userInfo')!);
      return initUserInfo;
    private getLatestReserved(agentNo) {
      const agentInfo = this.myConsultantList.filter(item => item.agentNo === agentNo);
      const appointmentInfo = agentInfo.length > 0 && agentInfo[0].appointments
        ? agentInfo[0].appointments!
              .filter((appointment) => appointment.communicateStatus === 'reserved')
              .map((reversedAppointment) => (
                { ...reversedAppointment,
                  sortDate: new Date(reversedAppointment.appointmentDate)
                }))
              .sort((preAppointment, nextAppointment) => +nextAppointment.sortDate - +preAppointment.sortDate)[0]
        : null;
      return this.getReservedData(appointmentInfo);
    }
    get isDisabledSubmitBtn(): boolean {
           return _.includes(this.myRequest.contactType,ContactType.PHONE)
      ? !this.isHopeContactTimeDone()
      : !this.phoneValid;
    private getReservedData(appointmentInfo) {
      if (appointmentInfo) {
        const hopeContactTime = appointmentInfo!.hopeContactTime
          ?  appointmentInfo!.hopeContactTime.split("'").filter(item => item && item !== ',').map(item => {
                const info = item.split('、');
                return {
                    selectWeekOptions: info[0].split(','),
                    selectTimesOptions: info[1].split(',')
                }
            })
          :[{selectWeekOptions : [],selectTimesOptions: []}];
        this.getAppointmentId(appointmentInfo);
        return {
            ...appointmentInfo,
            hopeContactTime: hopeContactTime,
            requirement: appointmentInfo.requirement
                        ? appointmentInfo.requirement.split(',')
                        : []
          }
      } else {
        return null;
      }
    }
    private isHopeContactTimeDone():boolean{
      return this.myRequest.hopeContactTime[0]?.selectWeekOptions.length >0 && this.myRequest.hopeContactTime[0]?.selectTimesOptions.length >0;
    private getAppointmentId(appointmentInfo) {
      this.appointmentId = appointmentInfo.id;
      this.appointmentTime = appointmentInfo.lastModifiedDate
                  ? appointmentInfo.lastModifiedDate
                  : appointmentInfo.appointmentDate;
    }
    sentDemand() {
      addFavoriteConsultant([this.$route.params.agentNo]).then(res => this.sentAppointmentDemand());
    ////////////////////////////////////////////////////////////////////////////
    async sentDemand() {
      if (!this.isEditBtn) {
        // 使用 async/await 來等待異步操作的回傳結果
        const addFavoriteAgentList = [{ agentNo: this.$route.params.agentNo, createdTime: new Date().toISOString() }];
        const response = await queryConsultantService.addFavoriteConsultant(addFavoriteAgentList);
        // 確保異步操作的回傳結果不為 null 或 undefined
        if (!response) {
          throw new Error('queryConsultantService.addFavoriteConsultant returned null-like value.');
        } else {
          if (typeof this.editAppointmentDemand === 'function') {
            // 確保 this.editAppointmentDemand 是一個函數
            await this.editAppointmentDemand();
          } else {
            // 處理 this.editAppointmentDemand 未定義的情況
            throw new Error('this.editAppointmentDemand is not defined or not a function.');
          }
        }
      } else {
        await this.editAppointmentDemand();
      }
      const editSettingInfo: UserSetting = {
        name: this.myRequest.name,
        phone: this.myRequest.phone,
        email: this.myRequest.email,
      };
      await accountSettingService.updateAccountSetting(editSettingInfo);
      this.storageUserInfo(this.userInfo);
    }
    private editAppointmentDemand() {
      const info = {
          ...this.myRequest,
          requirement: _.map(this.myRequest.requirement,o=>o).toString(),
          hopeContactTime: this.myRequest.phone && this.phoneValid ? this.getHopeContactTime() :'',
          id: this.appointmentId,
          otherRequirement: null
        }
      appointmentService.editAppointment(info).then(res => {
        this.sendReserve = true;
        this.myRequest.hopeContactTime = [];
        setRequestsToStorage(this.myRequest);
      });
    }
    private sentAppointmentDemand() {
@@ -304,17 +507,30 @@
          agentNo: this.$route.params.agentNo
        };
        appointmentDemand(data).then(res => {
        queryConsultantService.appointmentDemand(data).then(res => {
            this.sendReserve = true;
            this.myRequest.hopeContactTime = [];
            this.appointmentId = res['id'];
            setRequestsToStorage(this.myRequest);
        });
    }
    getHopeContactTime() {
        return this.myRequest.hopeContactTime.map(i => {
    private getHopeContactTime() {
        const selectedHopeContactTime = this.myRequest.hopeContactTime.filter((i) => i.selectWeekOptions?.length && i.selectTimesOptions?.length);
        return selectedHopeContactTime.map(i => {
            return `'${i.selectWeekOptions}、${i.selectTimesOptions}'`}
        ).toString();
    }
    reviewPlatform(): void {
      const reviewPlatformParams: UserReviewParams = {
        appointmentId: this.appointmentId,
        score: this.score,
        type: SatisfactionType.SYSTEM
      };
      reviewsService.reviewPlatform(reviewPlatformParams).then((_) => {
        this.closeReservePopUp();
      });
    }
    closeReservePopUp() {
@@ -322,34 +538,64 @@
        this.$router.push('/')
    }
    ////////////////////////////////////////////////////////////////////////////
    get phoneValid(): boolean {
      const rule = /^09[0-9]{8}$/;
      return this.myRequest.phone
            ? rule.test(this.myRequest.phone) && _.isEqual(this.myRequest.phone.length,10)
            : true;
    }
    get emailValid() {
      const rule = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
      return this.myRequest.email ? rule.test(this.myRequest.email) : true;
    }
    get userInfo(): RegisterInfo {
      const initUserInfo = JSON.parse(localStorage.getItem('userInfo')!);
      return initUserInfo;
    }
    get isDisabledSubmitBtn(): boolean {
           return _.includes(this.myRequest.contactType,ContactType.PHONE)
      ? !this.isHopeContactTimeDone() || !this.emailValid
      : !this.phoneValid;
    }
    private isHopeContactTimeDone():boolean{
      return this.myRequest.hopeContactTime[0]?.selectWeekOptions.length >0 && this.myRequest.hopeContactTime[0]?.selectTimesOptions.length >0;
    }
  }
</script>
<style lang="scss" scoped>
.sendReserve-txt{
    display: flex;
    justify-content: center;
    margin-top: 10px;
    margin-bottom: 26px;
  display: flex;
  justify-content: center;
  margin-top: 10px;
}
//drawer最底下文字樣式
.qa-dialog-footer{
    display: flex;
    justify-content: center;
    margin-bottom: 81px;
    color: #ED1B2E;
    cursor: pointer;
  display: flex;
  justify-content: center;
  margin-bottom: 81px;
  color: #ED1B2E;
  cursor: pointer;
}
.error {
  color:$PRIMARY_RED
}
//送出按鈕樣式與排版
.ques-footer{
    justify-content: center;
    margin: 30px 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    .el-button {
  justify-content: center;
  margin: 30px 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  .el-button {
    width: 120px;
    height:50px;
    background-color: #ED1B2E;
@@ -377,43 +623,42 @@
//詳細問題drawer中間內容空間大小設置
.qa-dialog{
    overflow-y:auto;
    height: 500px;
    margin-top: 20px;
  overflow-y:auto;
  height: 60vh;
  margin-top: 20px;
}
//詳細問題drawer主要標題
.qaTextTitle{
    margin-top:30px;
    display: flex;
    justify-content: center;
}
.el-button+.el-button{
    margin-left: 0;
  margin-top:30px;
  display: flex;
  justify-content: center;
}
.datepicker{
    display: flex;
    flex-direction: column;
  display: flex;
  flex-direction: column;
}
.required {
    position: relative;
    &::before {
        content: '*';
        position: absolute;
        color: #FF0000;
        transform: translate(-12px, 0);
    }
  position: relative;
  &::before {
      content: '*';
      position: absolute;
      color: #FF0000;
      transform: translate(-12px, 0);
  }
}
.break{
  word-break: break-all;
  line-height: 1.2;
}
.ques-page--reset.pam-page-container {
    margin: 0px auto;
  margin: 0px auto;
}
.ques-header {
    position: relative;
  position: relative;
}
.ques-header__mob-banner {
@@ -426,41 +671,53 @@
  background-position: center;
}
.ques-header__info {
  position: relative;
  padding:30px 20px;
  margin: 0px 20px;
  background-color: #B3E7E3;
  border-radius: 10px;
}
.ques-header__input-block {
  display: flex;
  align-items: center;
  @extend .text--middle,.mt-10 ;
  .ques-header__input{
    &.is-invalid{
      border: 2px solid $PRIMARY_RED !important;
    }
    flex: 1;
    height: 50px;
  .ques-header__info {
    position: relative;
    padding:30px 20px;
    margin: 0px 20px;
    background-color: #B3E7E3;
    border-radius: 10px;
    border: 1px #CCCCCC solid;
    background-color: $PRIMARY_WHITE;
    padding: 15px 10px;
    box-sizing: border-box;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
  }
  .ques-header__input-block {
    display: flex;
    align-items: center;
    @extend .text--middle,.mt-10 ;
    .ques-header__input{
      &.is-invalid{
        border: 1px solid $PRIMARY_RED !important;
      }
      flex: 1;
      height: 50px;
      border-radius: 10px;
      border: 1px #CCCCCC solid;
      background-color: $PRIMARY_WHITE;
      padding: 15px 10px;
      box-sizing: border-box;
      -webkit-box-sizing: border-box;
      -moz-box-sizing: border-box;
    }
  }
  .ml-4{
    margin-left: 4px;
  }
  .ques-container {
    position: relative;
    margin: 0px 20px;
  }
.pam-app-review{
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.ques-container {
  position: relative;
  margin: 0px 20px;
.pam-satisfaction-rate{
  margin-bottom: 45px;
}
@include desktop{
  @include desktop{
  .ques-header{
    display: flex;
    justify-content: flex-end;
@@ -486,5 +743,6 @@
  }
}
</style>