保誠-保戶業務員媒合平台
Mila
2022-01-24 741f1e1919adfa5745d945ce50beb1a317881f8e
Merge branch 'Phase3' of https://192.168.0.10:8443/r/pcalife/PAM into Phase3
修改6個檔案
287 ■■■■ 已變更過的檔案
PAMapp/assets/scss/utilities/_utilities.scss 5 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Client/ClientCard.vue 6 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/middleware/getUrlQuery.ts 12 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/index.vue 225 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/shared/models/strict-query.model.ts 18 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/store/localStorage.ts 21 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/scss/utilities/_utilities.scss
@@ -5,6 +5,10 @@
  margin-bottom: 50px;
}
.mt-50 {
  margin-top: 50px;
}
.mt-30 {
  margin-top: 30px;
}
@@ -21,6 +25,7 @@
  margin-bottom: 20px;
}
.mt-10 {
  margin-top: 10px;
}
PAMapp/components/Client/ClientCard.vue
@@ -261,12 +261,6 @@
    viewAppointmentDetail(): void {
      this.getAppointmentDetail(this.client.id).then((_) => {
        // setTimeout(() => {
        //   const unread = !this.client.consultantReadTime;
        //   if (unread) {
        //     this.readAppointment();
        //   }
        // }, 0);
        const unread = !this.client.consultantReadTime;
        if (unread) {
          this.readAppointment();
PAMapp/middleware/getUrlQuery.ts
@@ -3,7 +3,15 @@
const getUrlQuery: Middleware = (context) => {
  const currentRouteName = context.route.name;
  const satisfactionIdFromMsg = context.route.query.appointmentId;
  const queryNotContactAppointmentId = context.route.query.notContactAppointmentId;
  const isUserLogin = context.store.getters['localStorage/isUserLogin'];
  if (currentRouteName === 'index' && queryNotContactAppointmentId) {
    context.store.commit('localStorage/storageNotContactAppointmentIdFromMsg', queryNotContactAppointmentId);
    if (!isUserLogin) {
      context.redirect('/login');
    }
  }
  if (currentRouteName === 'index' && satisfactionIdFromMsg) {
    context.store.commit('localStorage/storageSatisfactionIdFromMsg', satisfactionIdFromMsg);
@@ -11,6 +19,8 @@
      context.redirect('/login');
    }
  }
}
export default getUrlQuery
export default getUrlQuery
PAMapp/pages/index.vue
@@ -41,10 +41,9 @@
    </div>
    <Ui-Dialog
        :isVisible.sync="isVisibleDialog"
        :width="width"
        :isVisible.sync="isShowAppointmentDialog"
        :width="appointmentDialogWidth"
        class="pam-myDemand-dialog pam-dialog-reserved"
        @closeDialog="clearSatisfactionId"
      >
        <div v-if="appointmentDetail">
            <h5 class="subTitle text--center mb-30">預約成功</h5>
@@ -73,14 +72,42 @@
            <div v-if="notScoreAppointmentYet" class="reserved-btn">
                <el-button type="primary"
                    @click.native="reviewsBtn = true">給予滿意度評分</el-button>
                    @click.native="isShowReviewDialog = true">給予滿意度評分</el-button>
            </div>
        </div>
      </Ui-Dialog>
      <PopUpFrame
        :isOpen.sync="reviewsBtn"
        @closePopUp="clearSatisfactionId"
        :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">
              保險顧問滿意度
@@ -110,11 +137,16 @@
<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 { UserReviewsConsultantsParams } from '~/shared/models/reviews.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 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';
  const localStorage = namespace('localStorage');
  const roleStorage = namespace('localStorage');
@@ -136,7 +168,8 @@
    @Action
    storeRecommendList!: any;
    @Action storeConsultantList!: any;
    @Action
    storeConsultantList!: any;
    @localStorage.Mutation
    storageClearQuickFilter!: () => void;
@@ -147,37 +180,55 @@
    @localStorage.Getter
    currentSatisfactionIdFromMsg!: string;
    @localStorage.Getter
    currentNotContactAppointmentIdFromMsg!: string;
    @localStorage.Mutation
    storageClearSatisfactionIdFromMsg!: () => void;
    @localStorage.Mutation
    storageClearNotContactAppointmentIdFromMsg!: () => void;
    @localStorage.Mutation
    storageStrickQueryItem!: (strictQueryDto: StrictQueryParams) => void;
    consultantList: Consultant[] = [];
    appointmentDetail: any = {
        age               : '',
        agentNo           : '',
        appointmentDate   : '',
        communicateStatus : '',
        consultantReadTime: null,
        consultantViewTime: null,
        contactTime       : '',
        contactType       : '',
        customerId        : 0,
        email             : '',
        gender            : '',
        hopeContactTime   : "",
        id                : 0,
        job               : "",
        lastModifiedDate  : '',
        name              : '',
        otherRequirement  : null,
        phone             : "",
        requirement       : '',
        satisfactionScore : 0,
    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,
    };
    isVisibleDialog = false;
    width = '';
    reviewsBtn = false;
    inputScore = 0;
    agentInfo: Consultant = {
      agentNo            : '',
      name               : '',
@@ -211,7 +262,7 @@
    }
    destroyed() {
      this.clearSatisfactionId();
      this.removeUrlQueryParameter();
    }
    //////////////////////////////////////////////////////////////////////
@@ -223,36 +274,81 @@
        .map((item) => ({ ...item, formatDate: new Date(item.updateTime || item.createTime)}))
        .sort((preItem, nextItem) => +nextItem.formatDate - +preItem.formatDate);
      if (this.currentSatisfactionIdFromMsg) {
        this.agentInfo = this.myConsultantList.filter(item => {
          const satisfactionIdIndex = item.appointments?.findIndex(i => i.id === +this.currentSatisfactionIdFromMsg);
          return satisfactionIdIndex !== undefined && satisfactionIdIndex > -1;
        })[0];
        if (this.agentInfo) {
          this.openAppointmentInfo();
        }
      if (this.currentNotContactAppointmentIdFromMsg) {
        this.autoOpenAppointmentBy('askReAppointment', +this.currentNotContactAppointmentIdFromMsg);
        return;
      }
      if (this.currentSatisfactionIdFromMsg) {
        this.autoOpenAppointmentBy('inviteReviewConsultant',+this.currentSatisfactionIdFromMsg);
        this.storageClearSatisfactionIdFromMsg();
        return;
      }
    }
    private openAppointmentInfo() {
        appointmentService.getAppointmentDetail(+this.currentSatisfactionIdFromMsg).then(res => {
            this.appointmentDetail = res;
            this.width = UtilsService.isMobileDevice() ? '80%' : '';
            this.isVisibleDialog = true;
            if (this.notScoreAppointmentYet) {
              setTimeout(() => {
                this.reviewsBtn = true;
              }, 500)
            }
    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);
    }
    reAppointment(): void {
      appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => {
        const requirements = this.appointmentDetail.requirement.split(',');
        console.log('requirements', requirements)
        this.storeConsultantList();
        this.storageStrickQueryItem({ requirements: requirements });
        this.storageClearNotContactAppointmentIdFromMsg();
        this.$router.push('/recommendConsultant');
      });
    }
    cancelAppointment(): void {
      appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => {
        this.storeConsultantList();
        this.storageClearNotContactAppointmentIdFromMsg();
        this.$router.push('');
      });
    }
    userReviewsConsultants() {
@@ -263,21 +359,22 @@
        this.appointmentDetail.satisfactionScore = this.inputScore;
        reviewsService.userReviewsConsultants(reviewParams).then((res) => {
            this.reviewsBtn = false;
            this.isShowReviewDialog = false;
        });
    }
    clearSatisfactionId() {
      // 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
    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 = {};
        Object.keys(this.$route.query).forEach((key) => {
          if (key !== 'appointmentId') {
            newRouteQuery[key] = this.$route.query[key]
          }
        })
        if (targetKey) {
          Object.keys(this.$route.query).forEach((key) => {
            if (key !== targetKey) {
              newRouteQuery[key] = this.$route.query[key]
            }
          })
        }
        this.$router.push(newRouteQuery);
        this.storageClearSatisfactionIdFromMsg();
    }
    ///////////////////////////////////////////////////////////////////////////////
PAMapp/shared/models/strict-query.model.ts
@@ -1,14 +1,14 @@
export interface StrictQueryParams {
  gender          : string;
  avgScore        : number;
  status          : string;    //phase 1 disable
  area            : string;
  requirements    : string[];
  otherRequirement: string;
  seniority       : string;
  popularTags     : string[];
  otherPopularTags: string;
  gender?          : string;
  avgScore?        : number;
  status?          : string;    //phase 1 disable
  area?            : string;
  requirements?    : string[];
  otherRequirement?: string;
  seniority?       : string;
  popularTags?     : string[];
  otherPopularTags?: string;
}
export interface AgentOfStrictQuery {
PAMapp/store/localStorage.ts
@@ -1,6 +1,7 @@
import { Module, Mutation, VuexModule ,Action } from 'vuex-module-decorators';
import { Role } from '~/shared/models/enum/Role';
import { Selected } from '~/shared/models/quick-filter.model';
import { StrictQueryParams } from '~/shared/models/strict-query.model';
@Module
export default class LocalStorage extends VuexModule {
  id_token = localStorage.getItem('id_token');
@@ -10,6 +11,7 @@
  recommendConsultantItem = localStorage.getItem('recommendConsultantItem');
  appointmentIdFromMsg = localStorage.getItem('appointmentIdFromMsg');
  satisfactionIdFromMsg = localStorage.getItem('satisfactionIdFromMsg');
  notContactAppointmentIdFromMsg = localStorage.getItem('notContactAppointmentIdFromMsg');
  get idToken(): string|null {
    return this.id_token;
@@ -41,6 +43,10 @@
  get currentSatisfactionIdFromMsg(): string|null {
    return this.satisfactionIdFromMsg;
  }
  get currentNotContactAppointmentIdFromMsg(): string|null {
    return this.notContactAppointmentIdFromMsg;
  }
  @Mutation storageIdToken(token: string): void {
@@ -78,6 +84,11 @@
    this.satisfactionIdFromMsg = localStorage.getItem('satisfactionIdFromMsg');
  }
  @Mutation storageNotContactAppointmentIdFromMsg(id: string) {
    localStorage.setItem('notContactAppointmentIdFromMsg', id);
    this.notContactAppointmentIdFromMsg = id;
  }
  @Mutation storageClear(): void {
    localStorage.removeItem('myRequests');
    localStorage.removeItem('userInfo');
@@ -112,6 +123,16 @@
    this.appointmentIdFromMsg = localStorage.getItem('satisfactionIdFromMsg');
  }
  @Mutation storageClearNotContactAppointmentIdFromMsg() {
    localStorage.removeItem('notContactAppointmentIdFromMsg');
    this.appointmentIdFromMsg = localStorage.getItem('notContactAppointmentIdFromMsg');
  }
  @Mutation storageStrickQueryItem(queryItem: StrictQueryParams): void {
    localStorage.setItem('recommendConsultantItem', JSON.stringify(queryItem));
    this.recommendConsultantItem = localStorage.getItem('recommendConsultantItem');
  }
  @Action actionStorageClear(): void {
    this.context.commit("storageClear");
  }