From 9ece54e8b526dc70b1a50014ee2c38e42bb403e4 Mon Sep 17 00:00:00 2001 From: Tomas <tomasysh@gmail.com> Date: 星期二, 25 一月 2022 12:25:43 +0800 Subject: [PATCH] update#134625: [ 客戶-我的顧問清單 ] 客戶安琪拉已預約熊蔡顧問,熊蔡顧問發送約訪通知,進入約訪中階段 , 但客戶安琪拉的顧問清單中 , 顧問熊蔡的狀態為「進行預約」 --- PAMapp/pages/index.vue | 560 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 453 insertions(+), 107 deletions(-) diff --git a/PAMapp/pages/index.vue b/PAMapp/pages/index.vue index 9a447a3..e424a82 100644 --- a/PAMapp/pages/index.vue +++ b/PAMapp/pages/index.vue @@ -1,138 +1,484 @@ <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> + <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.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> + </Ui-Dialog> + + <PopUpFrame + :isOpen.sync="isShowReAppointmentDialog" + @closePopUp="removeUrlQueryParameter('notContactAppointmentIdFromMsg')" + > + <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 { 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'; -@Component({ + 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 + @Action + storeRecommendList!: any; + + @Action + storeConsultantList!: any; + + @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, + }; + + agentInfo: Consultant = { + agentNo : '', + name : '', + img : '', + expertise : [], + avgScore : 0, + contactStatus : '', + createTime : '', + updateTime : '', + customerViewTime : '', + role : '', + seniority : '', + appointments : [] + }; + + ////////////////////////////////////////////////////////////////////// + + mounted() { + if (this.isAdminLogin) { + this.$router.push('/myAppointmentList/appointmentList'); + } else { + if (!this.recommendList?.length) { + this.storeRecommendList(); } + + this.storeConsultantList(); + this.storageClearQuickFilter(); + this.storageClearRecommendConsultant(); + } + } - 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 = 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(','); + this.storeConsultantList(); + this.storageStrickQueryItem({ requirements: requirements }); + this.storageClearNotContactAppointmentIdFromMsg(); + this.removeUrlQueryParameter('notContactAppointmentIdFromMsg'); + this.$router.push('/recommendConsultant'); + }); + } + + cancelAppointment(): void { + appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => { + this.storeConsultantList(); + this.storageClearNotContactAppointmentIdFromMsg(); + this.removeUrlQueryParameter('notContactAppointmentIdFromMsg'); + this.$router.push(''); + }); + } + + userReviewsConsultants() { + const reviewParams: UserReviewsConsultantsParams = { + appointmentId: this.appointmentDetail.id, + score: this.inputScore, + } + 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); + } + + /////////////////////////////////////////////////////////////////////////////// + + 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 !== ",") + } + + 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; + .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