保誠-保戶業務員媒合平台
Mila
2022-01-11 9c74f31c6e41de2bb2f6175a290f42d91968844e
update: TODO#132493 客戶端收到簡訊/EMAIL後進行滿意度評分
修改5個檔案
新增1個檔案
修改1個檔案名稱
295 ■■■■ 已變更過的檔案
PAMapp/assets/scss/vendors/elementUI/_dialog.scss 35 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Consultant/ConsultantCard.vue 44 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/middleware/errorRoute.ts 5 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/middleware/getUrlQuery.ts 12 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/nuxt.config.js 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/index.vue 182 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/store/localStorage.ts 15 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/scss/vendors/elementUI/_dialog.scss
@@ -35,3 +35,38 @@
    }
  }
}
.pam-dialog-review {
  .review-content {
    display: flex;
    flex-direction: row;
    justify-content: space-evenly;
  }
  .review-text {
    width: 60%;
    line-height: 28px;
    @extend .p;
    @extend .text--lighter;
  }
  .review-score {
    display: flex;
    justify-content: center;
    margin-bottom: 30px;
  }
  .review-btn {
    display: flex;
    justify-content: center;
  }
}
.pam-dialog-reserved {
  .reserved-info {
    font-size: 20px;
    overflow-y:scroll;
    height: 400px;
  }
  .reserved-btn {
    display: flex;
    justify-content: center;
  }
}
PAMapp/components/Consultant/ConsultantCard.vue
@@ -53,12 +53,12 @@
        <Ui-Dialog
            :isVisible.sync="isVisibleDialog"
            :width="width"
            class="pam-myDemand-dialog"
            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="dialogInfo">
                <div class="reserved-info">
                    <p>姓名:{{appointmentDetail.name}}</p>
                    <p>電話:{{appointmentDetail.phone}}</p>
                    <p>Email:{{appointmentDetail.email}}</p>
@@ -80,7 +80,7 @@
                    </div>
                </div>
                <div v-if="notScoreAppointmentYet" class="dialogInfo-btn">
                <div v-if="notScoreAppointmentYet" class="reserved-btn">
                    <el-button type="primary"
                        @click.native="reviewsBtn = true">給予滿意度評分</el-button>
                </div>
@@ -92,11 +92,11 @@
            </div>
        </Ui-Dialog>
        <PopUpFrame :isOpen.sync="reviewsBtn">
            <div class="mdTxt">
        <PopUpFrame :isOpen.sync="reviewsBtn" class="reviewDialog-content">
            <div class="mdTxt pam-dialog-review">
                ä¿éšªé¡§å•æ»¿æ„åº¦
                <span class="hint">選取星星</span>
                <div class="mt-30 reviewDialog-content">
                <div class="mt-30 review-content">
                    <UiAvatar :size="80" :agentNo="agentInfo.agentNo"></UiAvatar>
                    <div class="review-text">對於顧問
                        <span class="text--primary">{{agentInfo.name}}</span>
@@ -104,11 +104,11 @@
                    </div>
                </div>
                <div class="dialogInfo-score">
                <div class="review-score">
                    <el-rate v-model="inputScore" class="pam-rate mt-30"></el-rate>
                </div>
                <div class="dialogInfo-btn">
                <div class="review-btn">
                    <el-button
                        type="primary"
                        :disabled="!inputScore"
@@ -451,32 +451,4 @@
        flex-direction: column;
        justify-content: space-between;
    }
    .dialogInfo {
        font-size: 20px;
        overflow-y:scroll;
        height: 400px;
    }
    .dialogInfo-btn{
        display: flex;
        justify-content: center;
    }
    .dialogInfo-score{
        display: flex;
        justify-content: center;
        margin-bottom: 30px;
    }
    .reviewDialog-content {
        display: flex;
        flex-direction: row;
        justify-content: space-evenly;
        .review-text {
            width: 60%;
            line-height: 28px;
            @extend .p;
            @extend .text--lighter;
        }
    }
</style>
PAMapp/middleware/errorRoute.ts
File was renamed from PAMapp/middleware/errorRouteMiddleware.ts
@@ -1,6 +1,7 @@
import { Middleware } from '@nuxt/types';
const errorRouteMiddleware: Middleware = (context) => {
const errorRoute: Middleware = (context) => {
  if (!context.route.name) {
    const isAdminLogin = context.store.getters['localStorage/isAdminLogin'];
    if (isAdminLogin) {
@@ -11,4 +12,4 @@
  }
}
export default errorRouteMiddleware
export default errorRoute;
PAMapp/middleware/getUrlQuery.ts
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1,12 @@
import { Middleware } from '@nuxt/types';
const getUrlQuery: Middleware = (context) => {
  const currentRouteName = context.route.name;
  const satisfactionIdFromMsg = context.route.query.appointmentId;
  if (currentRouteName === 'index' && satisfactionIdFromMsg) {
    context.store.commit('localStorage/storageSatisfactionIdFromMsg', satisfactionIdFromMsg);
  }
}
export default getUrlQuery
PAMapp/nuxt.config.js
@@ -70,6 +70,6 @@
  },
  router: {
    base: process.env.ENV === 'uat' ? '/pam/' : '',
    middleware: 'errorRouteMiddleware'
    middleware: ['getUrlQuery', 'errorRoute']
  }
}
PAMapp/pages/index.vue
@@ -39,12 +39,82 @@
        <ConsultantSwiper :agents="recommendList"></ConsultantSwiper>
      </div>
    </div>
    <Ui-Dialog
        :isVisible.sync="isVisibleDialog"
        :width="width"
        class="pam-myDemand-dialog pam-dialog-reserved"
        @closeDialog="clearSatisfactionId"
      >
        <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.satisfactionScore" class="reserved-btn">
                <el-button type="primary"
                    @click.native="reviewsBtn = true">給予滿意度評分</el-button>
            </div>
        </div>
      </Ui-Dialog>
      <PopUpFrame
        :isOpen.sync="reviewsBtn"
        @closePopUp="clearSatisfactionId"
      >
          <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, Watch, namespace } from 'nuxt-property-decorator';
  import { Consultant } from '~/shared/models/consultant.model';
import { UserReviewsConsultantsParams } from '~/shared/models/reviews.model';
  import appointmentService from '~/shared/services/appointment.service';
import reviewsService from '~/shared/services/reviews.service';
  import UtilsService from '~/shared/services/utils.service';
  const localStorage = namespace('localStorage');
@@ -70,7 +140,54 @@
    @localStorage.Mutation
    storageClearRecommendConsultant!: () => void;
    @localStorage.Getter
    currentSatisfactionIdFromMsg!: string;
    @localStorage.Mutation
    storageClearSatisfactionIdFromMsg!: () => 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,
    };
    isVisibleDialog = false;
    width = '';
    reviewsBtn = false;
    inputScore = 0;
    agentInfo: Consultant = {
      agentNo            : '',
      name               : '',
      img                : '',
      expertise          : [],
      avgScore           : 0,
      contactStatus      : '',
      createTime         : '',
      updateTime         : '',
      customerViewTime   : '',
      role               : '',
      seniority          : '',
      appointments       : []
    };
    //////////////////////////////////////////////////////////////////////
@@ -84,6 +201,10 @@
      this.storageClearRecommendConsultant();
    }
    destroyed() {
      this.clearSatisfactionId();
    }
    //////////////////////////////////////////////////////////////////////
    @Watch('myConsultantList')
@@ -91,13 +212,71 @@
      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);
      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();
        }
      }
    }
    private openAppointmentInfo() {
        appointmentService.getAppointmentDetail(+this.currentSatisfactionIdFromMsg).then(res => {
            this.appointmentDetail = res;
            this.width = UtilsService.isMobileDevice() ? '80%' : '';
            this.isVisibleDialog = true;
            if (!this.appointmentDetail.satisfactionScore) {
              setTimeout(() => {
                this.reviewsBtn = true;
              }, 500)
            }
        });
    }
    //////////////////////////////////////////////////////////////////////
    navigateToRoute(path: string): void {
      this.$router.push(path);
    }
    userReviewsConsultants() {
      const reviewParams: UserReviewsConsultantsParams = {
            appointmentId: this.appointmentDetail.id,
            score: this.inputScore,
        }
        this.appointmentDetail.satisfactionScore = this.inputScore;
        reviewsService.userReviewsConsultants(reviewParams).then((res) => {
            this.reviewsBtn = false;
        });
    }
    clearSatisfactionId() {
        console.log('close');
        this.$router.push({query: {}});
        this.storageClearSatisfactionIdFromMsg();
    }
    ///////////////////////////////////////////////////////////////////////////////
    get gender() {
        if (this.appointmentDetail.gender) {
            return this.appointmentDetail.gender === 'male' ? '男性' : '女性';
        }
        return ''
    }
    get hopeContactTime() {
        const contactList = this.appointmentDetail.hopeContactTime
            .split("'").map((item: any) => item.slice(0, item.length));
        return contactList.filter((item: any) => !!item && item !== ",")
    }
  }
@@ -182,5 +361,4 @@
      max-width: 335px;
    }
  }
</style>
PAMapp/store/localStorage.ts
@@ -9,6 +9,7 @@
  quickFilterSelectedItem = localStorage.getItem('quickFilter');
  recommendConsultantItem = localStorage.getItem('recommendConsultantItem');
  appointmentIdFromMsg = localStorage.getItem('appointmentIdFromMsg');
  satisfactionIdFromMsg = localStorage.getItem('satisfactionIdFromMsg');
  get idToken(): string|null {
    return this.id_token;
@@ -36,6 +37,10 @@
  get currentAppointmentIdFromMsg(): string|null {
    return this.appointmentIdFromMsg;
  }
  get currentSatisfactionIdFromMsg(): string|null {
    return this.satisfactionIdFromMsg;
  }
  @Mutation storageIdToken(token: string): void {
@@ -68,6 +73,11 @@
    this.appointmentIdFromMsg = localStorage.getItem('appointmentIdFromMsg');
  }
  @Mutation storageSatisfactionIdFromMsg(id: string) {
    localStorage.setItem('satisfactionIdFromMsg', id);
    this.satisfactionIdFromMsg = localStorage.getItem('satisfactionIdFromMsg');
  }
  @Mutation storageClear(): void {
    localStorage.removeItem('myRequests');
    localStorage.removeItem('userInfo');
@@ -95,6 +105,11 @@
    this.appointmentIdFromMsg = localStorage.getItem('appointmentIdFromMsg');
  }
  @Mutation storageClearSatisfactionIdFromMsg() {
    localStorage.removeItem('satisfactionIdFromMsg');
    this.appointmentIdFromMsg = localStorage.getItem('satisfactionIdFromMsg');
  }
  @Action actionStorageClear(): void {
    this.context.commit("storageClear");
  }