保誠-保戶業務員媒合平台
HelenHuang
2022-06-09 9bdb95c9e34cef640534e5e5a1e2225a80442000
PAMapp/pages/index.vue
@@ -1,190 +1,514 @@
<template>
    <div>
        <el-button @click="login">登入</el-button>
        <el-button @click="remove">登出</el-button>
        <Ui-Carousel></Ui-Carousel>
        <div class="page-container">
            <h5 class="mdTxt mb-30">預約保險顧問</h5>
            <el-button
                class="reserveBtn recommendConsultant"
                @click="routerPush('/recommendConsultant')"
            >
                <p>嚴選配對</p>
            </el-button>
            <el-button
                class="reserveBtn quickFilter"
                @click="routerPush('/quickFilter')"
            >
                <p>快速篩選</p>
            </el-button>
            <el-row class="mb-30 rowStyle">
                <el-col :span="16">
                    <span class="mdTxt">我的顧問清單</span>
                    <span class="smTxt_bold amount">共 {{consultantList.length}} 筆</span>
                </el-col>
                <el-col :span="8" class="mdTxt readMore"
                    @click.native="routerPush('/myConsultantList/consultantList')">查看更多</el-col>
            </el-row>
            <ConsultantList
                :agents="consultantList.slice(0, 3)"
                @removeAgent="removeAgent"
            ></ConsultantList>
            <div class='pam-recommend pb-30 pt-30'>
                <h5 class="mdTxt">推薦保險顧問</h5>
                <img class="absulate img" src="~/assets/images/index_recommend.svg" alt="">
            </div>
            <ConsultantSwiper :agents="recommendList"></ConsultantSwiper>
  <div>
    <Ui-Carousel></Ui-Carousel>
    <div class="page-container">
      <div>
        <h5 class="mdTxt">預約保險顧問</h5>
        <div class="mt-10 pam-reserveBtn--block">
          <el-button class="reserveBtn recommendConsultant"
            @click="navigateToRoute('/recommendConsultant')">
            <p>嚴選配對</p>
          </el-button>
          <el-button class="reserveBtn quickFilter"
            @click="navigateToRoute('/quickFilter')">
            <p>快速篩選</p>
          </el-button>
        </div>
      </div>
      <div class="pam-paragraph">
        <el-row class="rowStyle">
          <el-col :span="16">
            <span class="mdTxt">我的顧問清單</span>
            <span class="smTxt_bold amount">共 {{ consultantList.length }} 筆</span>
          </el-col>
          <el-col :span="8"
            class="mdTxt readMore fix-chrome-click--issue"
            v-if="consultantList.length > 3"
            @click.native="navigateToRoute('/myConsultantList/consultantList')">查看更多</el-col>
        </el-row>
        <ConsultantList class="mt-10"
          :agents="consultantList.slice(0, 3)"></ConsultantList>
      </div>
      <div class='pam-paragraph'>
        <div class="pam-recommend">
          <h5 class="mdTxt">推薦保險顧問</h5>
          <img class="img"
            src="~/assets/images/index_recommend.svg"
            alt="">
        </div>
        <ConsultantSwiper :agents="recommendList"></ConsultantSwiper>
      </div>
    </div>
    <Ui-Dialog
        :isVisible.sync="isShowAppointmentDialog"
        :width="appointmentDialogWidth"
        class="pam-myDemand-dialog pam-dialog-reserved"
      >
          <div v-if="appointmentDetail">
              <h5 class="subTitle text--center mb-30">預約成功</h5>
              <p class="smTxt">{{appointmentDetail.appointmentDate | formatDate}}</p>
              <div class="reserved-info">
                  <p>姓名:{{appointmentDetail.name}}</p>
                  <p>電話:{{appointmentDetail.phone}}</p>
                  <p>Email:{{appointmentDetail.email}}</p>
                  <p>性別:{{gender}}</p>
                  <p>年齡:{{appointmentDetail.age | toAgeLabel }}</p>
                  <p>職業:{{appointmentDetail.job}}</p>
                  <p>需求:{{appointmentDetail.requirement.split(',').join('、')}}</p>
                  <p
                      v-for="(item, index) in hopeContactTime"
                      :key="index"
                  >連絡時段{{index + 1 | formatNumber}}:{{ item | formatHopeContactTime }}</p>
                  <div v-if="appointmentDetail.satisfactionScore">
                      <div class="mdTxt mt-10 mb-10">滿意度</div>
                      <el-rate
                      :value="appointmentDetail.satisfactionScore"
                      class="pam-myDemand-dialog__rate"
                      disabled>
                      </el-rate>
                  </div>
              </div>
              <div v-if="notScoreAppointmentYet" class="reserved-btn">
                  <el-button type="primary"
                      @click.native="reviewsBtn = true">給予滿意度評分</el-button>
              </div>
              <div v-if="appointmentDetail.communicateStatus === 'reserved'" class="text--center mt-10">
                  <el-button @click="isCancelPopup = true">取消預約</el-button>
                  <el-button @click="edit" type="primary">編輯</el-button>
              </div>
          </div>
      </Ui-Dialog>
      <PopUpFrame
        :isOpen.sync="isShowReAppointmentDialog"
        @closePopUp="removeUrlQueryParameter('notContactAppointmentId')"
      >
          <div class="pam-dialog-review">
              <div class="mt-30 text--middle" v-if="agentInfo">
                很抱歉!您預約的<span class="text--bold">{{ consultantName }}</span>顧問正忙碌中,請您取消預約並改選其他顧問
              </div>
                <el-row
                  type="flex"
                  class="mt-50"
                  justify="center">
                  <el-button
                      type="primary"
                      @click="reAppointment">取消預約再改選其他顧問</el-button>
                </el-row>
                <el-row
                  type="flex"
                  class="mt-20"
                  justify="center">
                  <el-button
                      class="outline_btn"
                      @click="cancelAppointment">取消預約</el-button>
                </el-row>
          </div>
      </PopUpFrame>
      <PopUpFrame
        :isOpen.sync="isShowReviewDialog"
        @closePopUp="removeUrlQueryParameter('appointmentId')"
      >
          <div class="mdTxt pam-dialog-review">
              保險顧問滿意度
              <span class="hint">選取星星</span>
              <div class="mt-30 review-content" v-if="agentInfo">
                  <UiAvatar :size="80" :agentNo="agentInfo.agentNo"></UiAvatar>
                  <div class="review-text">對於顧問
                      <span class="text--primary">{{agentInfo.name}}</span>
                      的整體服務,您給予幾顆星評價?
                  </div>
              </div>
              <div class="review-score">
                  <el-rate v-model="inputScore" class="pam-rate mt-30"></el-rate>
              </div>
              <div class="review-btn">
                  <el-button
                      type="primary"
                      :disabled="!inputScore"
                      @click="userReviewsConsultants">送出</el-button>
              </div>
          </div>
      </PopUpFrame>
  </div>
</template>
<script lang="ts">
import { Vue, Component, State, Action } from 'nuxt-property-decorator';
import { Consultants } from '~/assets/ts/api/consultant';
import { login, recommend, getFavoriteConsultant } from '~/assets/ts/api/consultant';
import { isLogin } from '~/assets/ts/auth';
  import { Vue, Component, State, Action, Watch, namespace } from 'nuxt-property-decorator';
@Component({
  import appointmentService from '~/shared/services/appointment.service';
  import reviewsService from '~/shared/services/reviews.service';
  import UtilsService from '~/shared/services/utils.service';
  import myConsultantService from '~/shared/services/my-consultant.service';
  import { Appointment, AppointmentClosedInfo } from '~/shared/models/appointment.model';
  import { Consultant } from '~/shared/models/consultant.model';
  import { UserReviewParams } from '~/shared/models/reviews.model';
  import { StrictQueryParams } from '~/shared/models/strict-query.model';
  import { AgentInfo } from '~/shared/models/agent-info.model';
  import { ContactStatus } from '~/shared/models/enum/contact-status';
import { SatisfactionType } from '~/shared/models/enum/satisfaction-type';
  const localStorage = namespace('localStorage');
  const roleStorage = namespace('localStorage');
  @Component({
    layout: 'home'
})
export default class MainComponent extends Vue {
    consultantList: Consultants[] = [];
    agents: Consultants[] = [];
    @State('recommendList') recommendList!: Consultants[];
    @Action storeRecommendList!: any;
  })
  export default class MainComponent extends Vue {
    @State('recommendList')
    recommendList!: Consultant[];
    @State('myConsultantList')
    myConsultantList!: Consultant[];
    @roleStorage.Getter
    isAdminLogin!: boolean;
    @roleStorage.Getter
    isUserLogin!: boolean;
    @Action
    storeRecommendList!: any;
    @Action
    storeConsultantList!: any;
    @localStorage.Mutation
    storageClearQuickFilter!: () => void;
    @localStorage.Mutation
    storageClearRecommendConsultant!: () => void;
    @localStorage.Getter
    currentSatisfactionIdFromMsg!: string;
    @localStorage.Getter
    currentNotContactAppointmentIdFromMsg!: string;
    @localStorage.Mutation
    storageClearSatisfactionIdFromMsg!: () => void;
    @localStorage.Mutation
    storageClearNotContactAppointmentIdFromMsg!: () => void;
    @localStorage.Mutation
    storageStrickQueryItem!: (strictQueryDto: StrictQueryParams) => void;
    consultantList: Consultant[] = [];
    appointmentDialogWidth    = '';
    inputScore                = 0;
    isShowAppointmentDialog   = false;
    isShowReAppointmentDialog = false;
    isShowReviewDialog        = false;
    consultantName = '';
    contactStatus = ContactStatus;
    appointmentDetail: Appointment = {
      age               : '',
      agentNo           : '',
      appointmentClosedInfo: {} as AppointmentClosedInfo,
      appointmentDate   : '',
      appointmentMemoList: [],
      appointmentNoticeLogs: [],
      communicateStatus : this.contactStatus.PICKED,
      consultantReadTime: '',
      consultantViewTime: '',
      contactTime       : '',
      contactType       : '',
      customerId        : 0,
      email             : '',
      gender            : '',
      hopeContactTime   : '',
      interviewRecordDTOs: [],
      id                : 0,
      job               : '',
      lastModifiedDate  : '',
      name              : '',
      otherRequirement  : '',
      phone             : '',
      requirement       : '',
      satisfactionScore : 0,
    };
    agentInfo: Consultant = {
      agentNo            : '',
      name               : '',
      img                : '',
      expertise          : [],
      avgScore           : 0,
      contactStatus      : '',
      createTime         : '',
      updateTime         : '',
      customerViewTime   : '',
      role               : '',
      seniority          : '',
      appointments       : []
    };
    //////////////////////////////////////////////////////////////////////
    mounted() {
        if (!this.recommendList) {
            this.storeRecommendList();
      if (this.isAdminLogin) {
        this.$router.push('/myAppointmentList/appointmentList');
      } else {
        if (!this.recommendList?.length) {
          this.storeRecommendList();
        }
        if (isLogin()) {
            getFavoriteConsultant().then((response) => this.consultantList = response.data);
        this.storeConsultantList();
        this.storageClearQuickFilter();
        this.storageClearRecommendConsultant();
        if (this.isUserLogin) {
          appointmentService.getNotContactAppointment().then((appointment) => {
            if (appointment) {
              this.$router.push({ query: { notContactAppointmentId: appointment.id + ''}});
              this.autoOpenAppointmentBy('askReAppointment', appointment.id);
            }
          });
        }
      }
    }
    routerPush(path: string) {
        this.$router.push(path);
    destroyed() {
      this.removeUrlQueryParameter();
    }
    removeAgent(agentNo: number) {
        const findIndex = this.consultantList.findIndex((item, i) => {
            return item.agentNo === agentNo;
        })
        this.consultantList.splice(findIndex, 1)
    //////////////////////////////////////////////////////////////////////
    @Watch('myConsultantList')
    onMyConsultantListChange() {
      this.consultantList = (this.myConsultantList || [])
        .map((item) => ({ ...item, formatDate: new Date(item.updateTime || item.createTime)}))
        .sort((preItem, nextItem) => +nextItem.formatDate - +preItem.formatDate);
      if (this.currentNotContactAppointmentIdFromMsg) {
        this.autoOpenAppointmentBy('askReAppointment', +this.currentNotContactAppointmentIdFromMsg);
        return;
      }
      if (this.currentSatisfactionIdFromMsg) {
        this.autoOpenAppointmentBy('inviteReviewConsultant',+this.currentSatisfactionIdFromMsg);
        this.storageClearSatisfactionIdFromMsg();
        return;
      }
    }
    // TODO: 僅OTP認證開發前 暫時使用
    login() {
        const user = {
            username: "user",
            password: "user"
    private autoOpenAppointmentBy(reason: string, targetAppointmentId: number): void {
        const setAgentInfo = new Promise((resolve, reject) => {
          this.agentInfo = this.myConsultantList.filter(item => {
            const appointmentIndex = item.appointments?.findIndex(i => i.id === targetAppointmentId);
            return appointmentIndex !== undefined && appointmentIndex > -1;
          })[0];
          if (this.agentInfo) {
            myConsultantService.getConsultantDetail(this.agentInfo.agentNo).then((res) => resolve(res));
          }
        });
        const setAppointment = new Promise((resolve, reject) => {
           appointmentService.getAppointmentDetail(targetAppointmentId).then((res) => resolve(res));
        });
        Promise.all([setAgentInfo, setAppointment]).then((values) => {
          const agentInfo = values[0] as AgentInfo;
          const appointmentInfo = values[1] as Appointment;
          this.consultantName = agentInfo.name;
          this.appointmentDetail = appointmentInfo;
          this.appointmentDialogWidth = UtilsService.isMobileDevice() ? '80%' : '';
          this.isShowAppointmentDialog = true;
          switch (reason) {
            case 'inviteReviewConsultant':
              if (this.notScoreAppointmentYet) {
                setTimeout(() => {
                  this.isShowReviewDialog = true;
                }, 500);
              }
              break;
            case 'askReAppointment':
              setTimeout(() => {
                this.isShowReAppointmentDialog = true;
              }, 500);
              break;
          }
        });
    }
    //////////////////////////////////////////////////////////////////////
    navigateToRoute(path: string): void {
      this.$router.push(path);
    }
    edit() {
        this.isShowAppointmentDialog = false;
        this.$router.push({path: `/questionnaire/${this.agentInfo.agentNo}`, query: {'edit': 'true'}});
    }
    reAppointment(): void {
      appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => {
        const requirements = this.appointmentDetail.requirement.split(',');
        this.storeConsultantList();
        this.storageStrickQueryItem({ requirements: requirements });
        this.storageClearNotContactAppointmentIdFromMsg();
        this.removeUrlQueryParameter('notContactAppointmentId');
        this.$router.push('/recommendConsultant');
      });
    }
    cancelAppointment(): void {
      appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => {
        this.storeConsultantList();
        this.storageClearNotContactAppointmentIdFromMsg();
        this.removeUrlQueryParameter('notContactAppointmentId');
        this.isShowReAppointmentDialog = false;
        this.isShowAppointmentDialog = false;
        this.$router.push('/');
      });
    }
    userReviewsConsultants() {
      const reviewParams: UserReviewParams = {
            appointmentId: this.appointmentDetail.id,
            score: this.inputScore,
            type: SatisfactionType.APPOINTMENT
        }
        login(user).then((res) => {
            localStorage.setItem('id_token', res.data.id_token);
            this.$router.go(0);
        })
        this.appointmentDetail.satisfactionScore = this.inputScore;
        reviewsService.userReviewsConsultants(reviewParams).then((res) => {
            this.isShowReviewDialog = false;
        });
    }
    // TODO: 僅OTP認證開發前 暫時使用
    remove() {
        localStorage.clear();
        this.$router.go(0)
    removeUrlQueryParameter(targetKey?: string): void {
        // NOTE: 刪除特定的 query parameter [Tomas, 2022/1/24 11:36]
        // [REF] How to remove a parameter from this.$router.query Nuxt.js? https://reurl.cc/X45aMD
        let newRouteQuery = {};
        if (targetKey) {
          Object.keys(this.$route.query).forEach((key) => {
            if (key !== targetKey) {
              newRouteQuery[key] = this.$route.query[key]
            }
          })
        }
        this.$router.push(newRouteQuery);
        this.storageClearSatisfactionIdFromMsg();
        this.storageClearNotContactAppointmentIdFromMsg();
    }
}
    ///////////////////////////////////////////////////////////////////////////////
    get gender(): string {
        if (this.appointmentDetail.gender) {
            return this.appointmentDetail.gender === 'male' ? '男性' : '女性';
        }
        return ''
    }
    get hopeContactTime(): string[] {
        const contactList = this.appointmentDetail.hopeContactTime
            .split("'").map((item: any) => item.slice(0, item.length));
        return contactList.filter((item: any) => !!item && item !== ",")
    }
    get notScoreAppointmentYet(): boolean {
      if (this.appointmentDetail.communicateStatus === 'closed' || this.appointmentDetail.communicateStatus === 'done') {
        return !this.appointmentDetail.satisfactionScore;
      };
      return false;
    }
  }
</script>
<style lang="scss" scoped>
  .page-container {
    margin: 0px 20px 30px 20px;
  }
  .reserveBtn.el-button--default {
    width              : 100%;
    height             : 110px;
    border-radius      : 10px;
    margin-bottom      : 15px;
    font-size          : 32px;
    font-weight        : 700;
    background-position: right;
    background-size    : cover;
    color              : #68737A;
    text-align         : left;
    background-repeat  : no-repeat;
    box-shadow         : 0px 0px 6px #a79b9b29;
    border-width       : 0;
    p {
      text-shadow: 1px 1px 5px $PRIMARY_WHITE;
    }
  }
  .el-button+.el-button {
    margin-left: 0px;
  }
  .quickFilter {
    background-image: url('~/assets/images/quickFilter/banner_mob.svg');
  }
  .recommendConsultant {
    background-image: url('~/assets/images/recommendConsultant/banner_mob.svg');
  }
  .rowStyle {
    .amount {
      color: $PRUDENTIAL_GREY;
    }
    .readMore {
      color: $PRIMARY_RED;
      cursor: pointer;
      text-align: right;
    }
  }
  .pam-recommend {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  @media (min-width: 576px) and (max-width: 767px) {
    .quickFilter.el-button--default {
      background-image: url('~/assets/images/quickFilter/banner_web.svg');
    }
    .recommendConsultant.el-button--default {
      background-image: url('~/assets/images/recommendConsultant/banner_web.svg');
    }
  }
  @include desktop {
    .page-container {
        padding: 0px 20px 30px 20px;
      width: 700px;
      margin: 0 auto;
    }
    .reserveBtn.el-button--default {
        width: 100%;
        height: 110px;
        border-radius: 10px;
        margin: 0 auto 17px auto;
        font-size: 32px;
        font-weight: 700;
        background-position: right;
        background-size: cover;
        color: #68737A;
        text-align: left;
        background-repeat: no-repeat;
        box-shadow: 0px 0px 6px #a79b9b29;
        border-width: 0;
        p {
            text-shadow: 1px 1px 5px $PRIMARY_WHITE;
        }
        &:nth-child(3) {
            margin-bottom: 42px;
        }
    .pam-reserveBtn--block {
      display: flex;
      justify-content: space-between;
    }
    .reserveBtn+.reserveBtn {
        margin-left: 0px;
    .reserveBtn {
      max-width: 335px;
    }
    .quickFilter {
        background-image: url('~/assets/images/quickFilter/banner_mob.svg');
    }
    .recommendConsultant {
        background-image: url('~/assets/images/recommendConsultant/banner_mob.svg');
    }
    @media (min-width: 576px) and (max-width: 1023px) {
        .quickFilter.el-button--default {
            background-image: url('~/assets/images/quickFilter/banner_web.svg');
        }
        .recommendConsultant.el-button--default {
            background-image: url('~/assets/images/recommendConsultant/banner_web.svg');
        }
    }
    .rowStyle {
        .amount {
            color: $PRUDENTIAL_GREY;
        }
        .readMore {
            color: $PRIMARY_RED;
            cursor: pointer;
            text-align: right;
        }
    }
    @include desktop {
        .page-container {
            width: 700px;
            margin: 0 auto;
        }
        .reserveBtn {
            max-width: 340px;
            &:nth-child(2) {
                margin-right: 15px;
            }
        }
    }
    .pam-recommend {
        position: relative;
        .img {
            position: absolute;
            right: 20px;
            bottom: 0px;
        }
    }
  }
</style>