From ab9d102ce99644f73ff365a1e70afbda1e14276d Mon Sep 17 00:00:00 2001
From: Tomas <tomasysh@gmail.com>
Date: 星期五, 28 七月 2023 12:03:32 +0800
Subject: [PATCH] Update: 顧問諮詢方式欄位(畫面)

---
 PAMapp/pages/index.vue |  616 ++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 509 insertions(+), 107 deletions(-)

diff --git a/PAMapp/pages/index.vue b/PAMapp/pages/index.vue
index 9a447a3..22cbb6f 100644
--- a/PAMapp/pages/index.vue
+++ b/PAMapp/pages/index.vue
@@ -1,138 +1,540 @@
 <template>
-    <div>
-        <Ui-Carousel></Ui-Carousel>
-        <div class="content">
-            <h5 class="mdTxt mb-20">����憿批��</h5>
-            <el-button
-                class="reserveConsultantBtn"
-                @click="routerPush('/recommendConsultant')"
-            >������</el-button>
-            <el-button
-                class="reserveConsultantBtn"
-                @click="routerPush('/quickFilter')"
-            >敹恍�祟�</el-button>
-            <div class="rowStyle mb-20">
-                <div class="flex">
-                    <h5 class="mdTxt">���“���</h5>
-                    <span class="smTxt_bold align_center amount">� {{agents.length}} 蝑�</span>
-                </div>
-                <div
-                    class="mdTxt readMore"
-                    @click="routerPush('/contactList/consultantList')"
-                >���憭�</div>
-            </div>
-            <ConsultantList
-                :agents="agents.slice(0, 3)"
-                @removeAgent="removeAgent"
-            ></ConsultantList>
-            <h5 class="mdTxt mb-20 mt-32">��靽憿批��</h5>
-            <Ui-Swiper :agents="swiperAgents"></Ui-Swiper>
-        </div>
+  <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.consultantMode }}</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>
 </template>
 
 <script lang="ts">
-import { Vue, Component } from 'nuxt-property-decorator';
-import { Agents } from '~/plugins/api/home';
-import { Context } from '@nuxt/types/app';
+import {Action, Component, Mutation, namespace, State, Vue, Watch} from 'nuxt-property-decorator';
 
-@Component({
+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 {
-    agents: Agents[] = [];
-    swiperAgents: Agents[] = [];
+  })
+  export default class MainComponent extends Vue {
 
-    async asyncData(context: Context) {
-        let agents: Agents[] = [];
+    @State('recommendList')
+    recommendList!: Consultant[];
 
-        await context.$service.home.recommendConsultantList().then((result: Agents[]) => {
-            agents = result;
-        })
+    @State('myConsultantList')
+    myConsultantList!: Consultant[];
 
-        const swiperAgents = JSON.parse(JSON.stringify(agents));
+    @roleStorage.Getter
+    isAdminLogin!: boolean;
 
-        return {
-            agents,
-            swiperAgents
+    @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[] = [];
+
+    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,
+      consultantMode    : ''
+    };
+
+    agentInfo: Consultant = {
+      agentNo            : '',
+      name               : '',
+      img                : '',
+      expertise          : [],
+      avgScore           : 0,
+      contactStatus      : '',
+      createTime         : '',
+      updateTime         : '',
+      customerViewTime   : '',
+      role               : '',
+      seniority          : '',
+      appointments       : []
+    };
+
+    //////////////////////////////////////////////////////////////////////
+
+    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);
+            }
+          });
+        }
+
+      }
+
     }
 
-    routerPush(path: string) {
-        this.$router.push(path);
+    destroyed() {
+      this.removeUrlQueryParameter();
     }
 
-    removeAgent(agentId: number) {
-        const findIndex = this.agents.findIndex((item, i) => {
-            return item.id === agentId;
-        })
-        this.agents.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;
+      }
     }
-}
+
+    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();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////
+
+    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;
+  }
 
-    .content {
-        padding: 0 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');
     }
 
-    .mb-20 {
-        margin: 0 0 20px 0;
+    .recommendConsultant.el-button--default {
+      background-image: url('~/assets/images/recommendConsultant/banner_web.svg');
+    }
+  }
+
+  @include desktop {
+    .page-container {
+      width: 700px;
+      margin: 0 auto;
     }
 
-    .mt-32 {
-        margin-top: 32px;
+      .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;
     }
 
-    .reserveConsultantBtn {
-        max-width: 340px;
-        width: 100%;
-        height: 110px;
-        border-radius: 10px;
-        box-shadow: 0px 0px 6px #22222229;
-        margin: 0 auto 17px auto;
-        font-size: 32px;
-        font-weight: 700;
-        color: $PRIMARY_BLACK;
-
-        &:nth-child(3) {
-            margin-bottom: 42px;
-        }
+    .reserveBtn {
+      max-width: 335px;
     }
-
-    .reserveConsultantBtn+.reserveConsultantBtn {
-        margin-left: 0px;
-    }
-
-    .rowStyle {
-        display: flex;
-        justify-content: space-between;
-        .flex {
-            display: flex;
-            .amount {
-                color: $PRUDENTIAL_GREY;
-                margin-left: 10px;
-                align-self: center;
-            }
-        }
-        .readMore {
-            color: $PRIMARY_RED;
-            cursor: pointer;
-        }
-    }
-
-    @include desktop {
-        .content{
-            width: 700px;
-            margin: 0 auto;
-        }
-
-        .reserveConsultantBtn:nth-child(2) {
-            margin-right: 15px;
-        }
-    }
-
+  }
 </style>

--
Gitblit v1.8.0