From 6054289ebe70ade6a245f6d40e48a84f9e6e0560 Mon Sep 17 00:00:00 2001
From: Tomas <tomasysh@gmail.com>
Date: 星期一, 31 七月 2023 14:49:02 +0800
Subject: [PATCH] Update: 更新諮詢方式相關 API 使用方式

---
 PAMapp/pages/index.vue |  489 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 453 insertions(+), 36 deletions(-)

diff --git a/PAMapp/pages/index.vue b/PAMapp/pages/index.vue
index 55baff7..d2dc803 100644
--- a/PAMapp/pages/index.vue
+++ b/PAMapp/pages/index.vue
@@ -2,33 +2,37 @@
   <div>
     <Ui-Carousel></Ui-Carousel>
     <div class="page-container">
-      <div class="mb-30">
+
+      <div>
         <h5 class="mdTxt">����憿批��</h5>
         <div class="mt-10 pam-reserveBtn--block">
           <el-button class="reserveBtn recommendConsultant"
-            @click="routerPush('/recommendConsultant')">
+            @click="navigateToRoute('/recommendConsultant')">
             <p>������</p>
           </el-button>
           <el-button class="reserveBtn quickFilter"
-            @click="routerPush('/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>
+            <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="routerPush('/myConsultantList/consultantList')">���憭�</el-col>
+            @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>
@@ -39,49 +43,425 @@
         <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.consultationMethod | toConsulType }}</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="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 class="video-container"
+         v-if="isShowFilmPlayer"
+         style="position: fixed; bottom: 30px; right: 30px; z-index: 9999; display: flex; justify-content: flex-end;"
+    >
+      <iframe width=��780��
+              height=��440��
+              allowfullscreen
+              src="https://www.youtube.com/embed/655JnwbuRGA?autoplay=1&mute=1"
+      ></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 { Consultant } from '~/assets/ts/models/consultant.model';
+import {Action, Component, 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({
     layout: 'home'
   })
   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;
+
+    @Mutation
+    setAccessSource!: (accessSource: AccessFroms) => void;
+
+    @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[] = [];
 
-    @State('recommendList') recommendList!: Consultant[];
-    @Action storeRecommendList!: any;
+    appointmentDialogWidth    = '';
+    inputScore                = 0;
+    isShowAppointmentDialog   = false;
+    isShowReAppointmentDialog = false;
+    isShowReviewDialog        = false;
+    consultantName = '';
+    contactStatus = ContactStatus;
 
-    @State('myConsultantList') myConsultantList!: Consultant[];
-    @Action storeConsultantList!: any;
+    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,
+      consultationMethod    : ''
+    };
 
-    @localStorage.Mutation storageClearQuickFilter!: () => void;
-    @localStorage.Mutation storageClearRecommendConsultant!: () => void;
+    agentInfo: Consultant = {
+      agentNo            : '',
+      name               : '',
+      img                : '',
+      expertise          : [],
+      avgScore           : 0,
+      contactStatus      : '',
+      createTime         : '',
+      updateTime         : '',
+      customerViewTime   : '',
+      role               : '',
+      seniority          : '',
+      appointments       : []
+    };
+
+    isShowFilmPlayer = true;
+
+    //////////////////////////////////////////////////////////////////////
+
+    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 {
+        if (!this.recommendList?.length) {
+          this.storeRecommendList();
+        }
+
+        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);
+            }
+          });
+        }
+
+      }
+
+    }
+
+    destroyed() {
+      this.removeUrlQueryParameter();
+    }
+
+    //////////////////////////////////////////////////////////////////////
 
     @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)
-    }
+        .sort((preItem, nextItem) => +nextItem.formatDate - +preItem.formatDate);
 
-    mounted() {
-      if (!this.recommendList?.length) {
-        this.storeRecommendList();
+      if (this.currentNotContactAppointmentIdFromMsg) {
+        this.autoOpenAppointmentBy('askReAppointment', +this.currentNotContactAppointmentIdFromMsg);
+        return;
       }
 
-      this.storeConsultantList();
-      this.storageClearQuickFilter();
-      this.storageClearRecommendConsultant();
+      if (this.currentSatisfactionIdFromMsg) {
+        this.autoOpenAppointmentBy('inviteReviewConsultant',+this.currentSatisfactionIdFromMsg);
+        this.storageClearSatisfactionIdFromMsg();
+        return;
+      }
     }
 
-    routerPush(path: string) {
+    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 = utilService.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 && this.appointmentDetail.requirement
+                            ? 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
+        }
+        this.appointmentDetail.satisfactionScore = this.inputScore;
+
+        reviewsService.userReviewsConsultants(reviewParams).then((res) => {
+            this.isShowReviewDialog = false;
+        });
+    }
+
+    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();
+    }
+
+    closeVideo() {
+      this.isShowFilmPlayer = false;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////
+
+    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;
     }
 
   }
@@ -94,19 +474,19 @@
   }
 
   .reserveBtn.el-button--default {
-    width: 100%;
-    height: 110px;
-    border-radius: 10px;
-    margin-bottom: 15px;
-    font-size: 32px;
-    font-weight: 700;
+    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;
+    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;
@@ -141,6 +521,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');
@@ -157,6 +538,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;
@@ -167,4 +557,31 @@
     }
   }
 
+  /* 蝣箔�� .video-container ��撠�/蝯����摰祝摨� */
+  .video-container {
+    position: relative;
+    width: 600px; /* �����蔣��偕撖貉矽� */
+  }
+
+  /* �����見撘� */
+  .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>

--
Gitblit v1.8.0