保誠-保戶業務員媒合平台
Tomas
2023-08-21 9e39fe1f194cd9d936bf1b6607b15d5dd6768137
PAMapp/pages/index.vue
@@ -2,6 +2,7 @@
  <div>
    <Ui-Carousel></Ui-Carousel>
    <div class="page-container">
      <div>
        <h5 class="mdTxt">預約保險顧問</h5>
        <div class="mt-10 pam-reserveBtn--block">
@@ -15,6 +16,7 @@
          </el-button>
        </div>
      </div>
      <div class="pam-paragraph">
        <el-row class="rowStyle">
          <el-col :span="16">
@@ -29,6 +31,8 @@
        <ConsultantList class="mt-10"
          :agents="consultantList.slice(0, 3)"></ConsultantList>
      </div>
      <div class='pam-paragraph'>
        <div class="pam-recommend">
          <h5 class="mdTxt">推薦保險顧問</h5>
@@ -45,36 +49,42 @@
        :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="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.consultationMethod | toConsultationMethod }}</p>
                  <p>需求:{{ appointmentDetail.requirement ? 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="isShowReviewDialog = true">給予滿意度評分</el-button>
            </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
@@ -132,23 +142,40 @@
              </div>
          </div>
      </PopUpFrame>
    <div class="video-container"
         v-if="isShowFilmPlayer"
         style="position: fixed; bottom: 30px; right: 30px; z-index: 9999; display: flex; justify-content: flex-end;"
    >
      <iframe
        id="ytplayer" type="text/html" width="360" height="270"
        frameborder="0"
        allowfullscreen
        sandbox="allow-scripts allow-same-origin allow-popups fullscreen"
        :src="filmVideoSrc"
      ></iframe>
      <div class="close-btn" @click="closeVideo()">X</div>
    </div>
  </div>
</template>
<script lang="ts">
  import { Vue, Component, State, Action, Watch, namespace } from 'nuxt-property-decorator';
  import { Appointment, AppointmentClosedInfo } from '~/shared/models/appointment.model';
  import { Consultant } from '~/shared/models/consultant.model';
  import { ContactStatus } from '~/shared/models/enum/contact-status';
  import { UserReviewsConsultantsParams } from '~/shared/models/reviews.model';
  import { StrictQueryParams } from '~/shared/models/strict-query.model';
  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 { AgentInfo } from '~/shared/models/agent-info.model';
import {Action, Component, Getter, Mutation, namespace, State, Vue, Watch} from 'nuxt-property-decorator';
  const localStorage = namespace('localStorage');
import appointmentService from '~/shared/services/appointment.service';
import utilService, {AccessFroms} from '~/shared/services/utils.service';
import reviewsService from '~/shared/services/reviews.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({
@@ -165,11 +192,17 @@
    @roleStorage.Getter
    isAdminLogin!: boolean;
    @roleStorage.Getter
    isUserLogin!: boolean;
    @Action
    storeRecommendList!: any;
    @Action
    storeConsultantList!: any;
    @Mutation
    setAccessSource!: (accessSource: AccessFroms) => void;
    @localStorage.Mutation
    storageClearQuickFilter!: () => void;
@@ -209,7 +242,7 @@
      appointmentDate   : '',
      appointmentMemoList: [],
      appointmentNoticeLogs: [],
      communicateStatus : this.contactStatus.PICKED,
      communicateStatus: this.contactStatus.PICKED,
      consultantReadTime: '',
      consultantViewTime: '',
      contactTime       : '',
@@ -227,6 +260,7 @@
      phone             : '',
      requirement       : '',
      satisfactionScore : 0,
      consultationMethod    : ''
    };
    agentInfo: Consultant = {
@@ -244,9 +278,31 @@
      appointments       : []
    };
    isCloseVideo = false;
    @Getter
    fromAccess!: AccessFroms;
    get filmVideoSrc() {
      if (!this.fromAccess) return '';
      return this.fromAccess === AccessFroms.FILM_1
        ? 'https://www.youtube.com/embed/zVBLW9hWD0g?autoplay=1&mute=1&loop=1&playlist=zVBLW9hWD0g'
        : 'https://www.youtube.com/embed/655JnwbuRGA?autoplay=1&mute=1&loop=1&playlist=655JnwbuRGA';
    }
    get isShowFilmPlayer() {
      return !!this.fromAccess && !this.isCloseVideo;
    }
    //////////////////////////////////////////////////////////////////////
    mounted() {
      if (this.$route.query.from) {
        const fromSource = this.$route.query.from as AccessFroms;
        this.setAccessSource(fromSource);
        utilService.insertAccessFrom(fromSource);
      }
      if (this.isAdminLogin) {
        this.$router.push('/myAppointmentList/appointmentList');
      } else {
@@ -257,6 +313,16 @@
        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);
            }
          });
        }
      }
    }
@@ -270,7 +336,6 @@
    @Watch('myConsultantList')
    onMyConsultantListChange() {
      this.consultantList = (this.myConsultantList || [])
        .filter(item => item.contactStatus !== 'contacted')
        .map((item) => ({ ...item, formatDate: new Date(item.updateTime || item.createTime)}))
        .sort((preItem, nextItem) => +nextItem.formatDate - +preItem.formatDate);
@@ -306,7 +371,7 @@
          const appointmentInfo = values[1] as Appointment;
          this.consultantName = agentInfo.name;
          this.appointmentDetail = appointmentInfo;
          this.appointmentDialogWidth = UtilsService.isMobileDevice() ? '80%' : '';
          this.appointmentDialogWidth = utilService.isMobileDevice() ? '80%' : '';
          this.isShowAppointmentDialog = true;
          switch (reason) {
            case 'inviteReviewConsultant':
@@ -332,9 +397,16 @@
      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(',');
        const requirements = this.appointmentDetail && this.appointmentDetail.requirement
                            ? this.appointmentDetail.requirement.split(',')
                            : [];
        this.storeConsultantList();
        this.storageStrickQueryItem({ requirements: requirements });
        this.storageClearNotContactAppointmentIdFromMsg();
@@ -348,14 +420,17 @@
        this.storeConsultantList();
        this.storageClearNotContactAppointmentIdFromMsg();
        this.removeUrlQueryParameter('notContactAppointmentId');
        this.$router.push('');
        this.isShowReAppointmentDialog = false;
        this.isShowAppointmentDialog = false;
        this.$router.push('/');
      });
    }
    userReviewsConsultants() {
      const reviewParams: UserReviewsConsultantsParams = {
      const reviewParams: UserReviewParams = {
            appointmentId: this.appointmentDetail.id,
            score: this.inputScore,
            type: SatisfactionType.APPOINTMENT
        }
        this.appointmentDetail.satisfactionScore = this.inputScore;
@@ -376,18 +451,24 @@
          })
        }
        this.$router.push(newRouteQuery);
        this.storageClearSatisfactionIdFromMsg();
        this.storageClearNotContactAppointmentIdFromMsg();
    }
    closeVideo(): void {
      this.isCloseVideo = true;
    }
    ///////////////////////////////////////////////////////////////////////////////
    get gender() {
    get gender(): string {
        if (this.appointmentDetail.gender) {
            return this.appointmentDetail.gender === 'male' ? '男性' : '女性';
        }
        return ''
    }
    get hopeContactTime() {
    get hopeContactTime(): string[] {
        const contactList = this.appointmentDetail.hopeContactTime
            .split("'").map((item: any) => item.slice(0, item.length));
        return contactList.filter((item: any) => !!item && item !== ",")
@@ -457,6 +538,7 @@
    align-items: center;
  }
  @media (min-width: 576px) and (max-width: 767px) {
    .quickFilter.el-button--default {
      background-image: url('~/assets/images/quickFilter/banner_web.svg');
@@ -473,6 +555,15 @@
      margin: 0 auto;
    }
      .landing-container {
    border: 2px solid blue;
    border-radius: 10px;
    padding: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
    .pam-reserveBtn--block {
      display: flex;
      justify-content: space-between;
@@ -482,4 +573,32 @@
      max-width: 335px;
    }
  }
  /* 確保 .video-container 有相對/絕對定位或固定寬度 */
  .video-container {
    position: relative;
    width: auto; /* 根據你的影片尺寸調整 */
  }
  /* 關閉按鈕樣式 */
  .close-btn {
    position: absolute;
    top: -10px;
    right: -10px;
    cursor: pointer;
    font-size: 20px;
    color: #fff;
    background-color: #000;
    width: 30px;
    height: 30px;
    text-align: center;
    line-height: 30px;
    border-radius: 50%;
    opacity: 0.7;
  }
  .close-btn:hover {
    opacity: 1;
  }
</style>