update: TODO#134222/134105 通知小鈴鐺/取得待評分的滿意度清單, 串接api
| | |
| | | </div> |
| | | <div class="pam-header__action-bar"> |
| | | <i |
| | | v-if="currentRole" |
| | | v-if="isShowNotification" |
| | | class="icon-bell text--dark-blue cursor--pointer fix-chrome-click--issue" |
| | | @click="$router.push('/notification')" |
| | | ></i> |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component } from 'vue-property-decorator'; |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | import { Action, namespace, State, Watch } from 'nuxt-property-decorator'; |
| | | import { Role } from '~/shared/models/enum/Role'; |
| | | import * as _ from 'lodash'; |
| | | import { NotificationList } from '~/shared/models/reviews.model'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | @Component |
| | |
| | | |
| | | @roleStorage.Getter |
| | | isAdminLogin!: boolean; |
| | | |
| | | @roleStorage.Getter |
| | | isUserLogin!: boolean; |
| | | |
| | | @Action |
| | | storeMyPersonalNotification!: () => void; |
| | | |
| | | @State |
| | | notificationList!: NotificationList[]; |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog!: () => void; |
| | | |
| | | @State |
| | | unReviewLogList!: AppointmentLog[]; |
| | | |
| | | isOpenDropdown = false; |
| | | |
| | |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Watch('$route', {immediate: true}) |
| | | onRouterChange() { |
| | | this.getNotificationAndReviewLog(); |
| | | } |
| | | |
| | | private getNotificationAndReviewLog() { |
| | | if (this.isUserLogin) { |
| | | this.storeMyPersonalNotification(); |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | |
| | | if (this.isAdminLogin) { |
| | | this.storeMyPersonalNotification(); |
| | | } |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | routerNavigateTo(url: string): void { |
| | | (this.$refs.dropdown as any).hide(); |
| | | _.isEqual(url,'') |
| | |
| | | return this.idToken && this.currentRole ? (this.currentRole as Role): Role.NOT_LOGIN; |
| | | } |
| | | |
| | | get isShowNotification() { |
| | | if (this.isUserLogin) { |
| | | return this.notificationList.length || this.unReviewLogList.length; |
| | | } |
| | | if (this.isAdminLogin) { |
| | | return this.notificationList.length |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | </script> |
| | |
| | | </section> |
| | | <div class="user-reviews-page"> |
| | | |
| | | <template v-if="myAppointmentReviewLogList.length"> |
| | | <template v-if="reviewLogList.length"> |
| | | <section class="user-reviews-content"> |
| | | <div |
| | | class="user-reviews-card" |
| | | v-for="(appointmentLog, index) in myAppointmentReviewLogList" |
| | | v-for="(appointmentLog, index) in reviewLogList" |
| | | :key="index"> |
| | | <div class="user-reviews-card-content" v-if="isUserLogin"> |
| | | 您對<span class="mdTxt">{{ ` ${appointmentLog.agentName} ` }}</span>做了 <UiReviewScore :score="appointmentLog.score" /> 評價! |
| | |
| | | </template> |
| | | <script lang="ts"> |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | import { Vue, Component, Prop } from 'nuxt-property-decorator'; |
| | | import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'; |
| | | import authService from '~/shared/services/auth.service'; |
| | | |
| | | @Component |
| | | export default class ReviewRecords extends Vue{ |
| | | |
| | | @Prop() |
| | | myAppointmentReviewLogList!: AppointmentLog[]; |
| | | reviewLogList!: AppointmentLog[]; |
| | | |
| | | isUserLogin = false; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.isUserLogin = authService.isUserLogin(); |
| | | } |
| | |
| | | <div class="text--right mb-10" @click="showNotificationHint = true"> |
| | | <i class="satisfaction-icon icon-edit"></i> |
| | | </div> |
| | | <div class="satisfaction-banner my-10 cursor--pointer" @click="$router.push('/satisfactionList')"> |
| | | <div |
| | | v-if="isUserLogin && unReviewLogList.length" |
| | | class="satisfaction-banner my-10 cursor--pointer" |
| | | @click="$router.push('/satisfactionList')" |
| | | > |
| | | <p class="satisfaction-text text--center">請填寫滿意度調查</p> |
| | | </div> |
| | | <el-row |
| | |
| | | align="middle" |
| | | class="notification-card" |
| | | > |
| | | <el-col class="unRead" :span="3"></el-col> |
| | | <el-col class="unRead" :span="3" v-if="!item.readDate"></el-col> |
| | | <el-col :span="18"> |
| | | <p class="text">{{item.content}}</p> |
| | | </el-col> |
| | |
| | | <div> |
| | | <UiDateFormat |
| | | class="date" |
| | | :date="item.date" |
| | | :date="item.createdDate" |
| | | onlyShowSection="DAY" /> |
| | | </div> |
| | | <div> |
| | | <UiDateFormat |
| | | class="time" |
| | | :date="item.date" |
| | | :date="item.createdDate" |
| | | onlyShowSection="TIME" /> |
| | | </div> |
| | | |
| | |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, Vue } from "nuxt-property-decorator"; |
| | | import { Component, State, Vue } from "nuxt-property-decorator"; |
| | | import { AppointmentLog } from "~/shared/models/appointment.model"; |
| | | import { NotificationList } from "~/shared/models/reviews.model"; |
| | | import authService from "~/shared/services/auth.service"; |
| | | |
| | | @Component |
| | | export default class Notification extends Vue { |
| | | showNotificationHint = false; |
| | | |
| | | notificationList = [ |
| | | { |
| | | content: '系統停機公告:10/19(五)22:30至10/21(日)20:00進行系統更新', |
| | | date: '2022-01-05T04:18:05.249Z' |
| | | }, |
| | | { |
| | | content: '系統停機公告:10/19(五)22:30至10/21(日)20:00進行系統更新', |
| | | date: '2022-01-05T04:18:05.249Z' |
| | | } |
| | | ] |
| | | @State |
| | | unReviewLogList!: AppointmentLog[]; |
| | | |
| | | @State |
| | | notificationList!: NotificationList[]; |
| | | |
| | | showNotificationHint = false; |
| | | isUserLogin = false; |
| | | |
| | | //////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.isUserLogin = authService.isUserLogin(); |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | |
| | | <template> |
| | | <div> |
| | | <ReviewRecords |
| | | :myAppointmentReviewLogList="myAppointmentReviewLogList" |
| | | :reviewLogList="reviewLogList" |
| | | ></ReviewRecords> |
| | | </div> |
| | | </template> |
| | |
| | | @Component |
| | | export default class Reviews extends Vue{ |
| | | |
| | | @State('myAppointmentReviewLogList') |
| | | myAppointmentReviewLogList!: AppointmentLog[]; |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog!: any; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | @State('reviewLogList') |
| | | reviewLogList!: AppointmentLog[]; |
| | | |
| | | } |
| | | </script> |
| | |
| | | <div class="pam-container"> |
| | | <div class="satisfaction-title"> |
| | | <span class="mdTxt">滿意度調查</span> |
| | | <span class="ml-10 text--prudential_grey smTxt_bold">共 {{satisfactionList.length}} 筆</span> |
| | | <span class="ml-10 text--prudential_grey smTxt_bold">共 {{mapUnReviewLogList.length}} 筆</span> |
| | | </div> |
| | | <div class="satisfaction-card" v-for="(item, index) in satisfactionList" :key="index"> |
| | | <div class="satisfaction-card" v-for="(item, index) in mapUnReviewLogList" :key="index"> |
| | | <div class="satisfaction-card-content"> |
| | | <UiAvatar :size="80" :agentNo="''"></UiAvatar> |
| | | <UiAvatar :size="80" :agentNo="item.agentNo"></UiAvatar> |
| | | <div class="satisfaction-card-text">對於顧問 |
| | | <span class="text--primary text--bold">{{item.agentName}}</span> |
| | | 的整體服務,您給予幾顆星評價? |
| | | </div> |
| | | </div> |
| | | <el-rate v-model="item.score" class="pam-satisfaction-rate mt-10"></el-rate> |
| | | <el-rate v-model="item.satisfaction" class="pam-satisfaction-rate mt-10"></el-rate> |
| | | </div> |
| | | <div class="text--center mt-30"> |
| | | <el-button type="primary" :disabled="isBtnDisabled">送出</el-button> |
| | |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { Vue, Component } from 'nuxt-property-decorator'; |
| | | <script lang="ts"> |
| | | import { Vue, Component, Action, State, Watch } from 'nuxt-property-decorator'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | |
| | | @Component({ |
| | | layout: 'home' |
| | | }) |
| | | export default class MySatisfactionList extends Vue { |
| | | satisfactionList = [ |
| | | { |
| | | agentName: '蔡美眉', |
| | | score: 0 |
| | | }, |
| | | { |
| | | agentName: '蔡美眉', |
| | | score: 0 |
| | | |
| | | @State |
| | | unReviewLogList!: AppointmentLog[]; |
| | | |
| | | mapUnReviewLogList: AppointmentReviewLog[] = []; |
| | | |
| | | /////////////////////////////////////////////////////// |
| | | |
| | | @Watch('unReviewLogList') |
| | | onUnReviewLogListChange() { |
| | | if (this.unReviewLogList.length) { |
| | | this.mapUnReviewLogList = this.unReviewLogList.map(item => { |
| | | return { |
| | | ...item, |
| | | satisfaction: 0 |
| | | } |
| | | }) |
| | | } |
| | | ] |
| | | } |
| | | |
| | | /////////////////////////////////////////////////////// |
| | | |
| | | get isBtnDisabled() { |
| | | return this.satisfactionList.findIndex(item => item.score > 0) === -1; |
| | | if (this.mapUnReviewLogList.length) { |
| | | return this.mapUnReviewLogList.findIndex(item => item.satisfaction > 0) === -1; |
| | | } |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | interface AppointmentReviewLog extends AppointmentLog { |
| | | satisfaction: number; |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | |
| | | <template> |
| | | <div> |
| | | <ReviewRecords |
| | | :myAppointmentReviewLogList="myAppointmentReviewLogList" |
| | | :reviewLogList="reviewLogList" |
| | | ></ReviewRecords> |
| | | </div> |
| | | </template> |
| | |
| | | |
| | | @Component |
| | | export default class UserReviewsRecord extends Vue{ |
| | | @State('myAppointmentReviewLogList') |
| | | myAppointmentReviewLogList!: AppointmentLog[]; |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog!: any; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | mounted() { |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | @State('reviewLogList') |
| | | reviewLogList!: AppointmentLog[]; |
| | | |
| | | } |
| | | </script> |
| | |
| | | appointmentId: number, |
| | | score : number, |
| | | } |
| | | |
| | | export interface NotificationList { |
| | | id: number; |
| | | /** 標題 */ |
| | | title: string; |
| | | /** 通知內容 */ |
| | | content: string; |
| | | /** ACTIVITY(個人活動通知)、SYSTEM(系統通知) */ |
| | | notificationType: string; |
| | | /** CUSTOMER(客戶)、CONSULTANT(顧問) */ |
| | | ownerRole: string; |
| | | /** 登入者id */ |
| | | ownerId: number |
| | | createdDate: string; |
| | | /** 已讀時間 */ |
| | | readDate: string; |
| | | } |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { UserReviewsConsultantsParams } from "../models/reviews.model"; |
| | | import { NotificationList, UserReviewsConsultantsParams } from "../models/reviews.model"; |
| | | import { AppointmentLog } from "../models/appointment.model"; |
| | | |
| | | class ReviewsService { |
| | |
| | | sendSatisfactionToClient(appointmentId: number) { |
| | | return http.post(`/consultant/sendSatisfactionToClient/${appointmentId}`).then((res) => res); |
| | | } |
| | | |
| | | // 通知小鈴鐺 |
| | | getMyPersonalNotification(): Promise<NotificationList[]> { |
| | | return http.get('/personal_notification/getMyPersonalNotification').then(res => res.data); |
| | | } |
| | | } |
| | | |
| | | export default new ReviewsService(); |
| | |
| | | import { getFavoriteFromStorage, setFavoriteToStorage } from '~/shared/storageConsultant'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | import { AgentOfStrictQuery, StrictQueryParams } from '~/shared/models/strict-query.model'; |
| | | import { NotificationList } from '~/shared/models/reviews.model'; |
| | | |
| | | @Module |
| | | export default class Store extends VuexModule { |
| | |
| | | strictQueryList: AgentOfStrictQuery[] = []; |
| | | myConsultantList: Consultant[] = []; |
| | | |
| | | myAppointmentReviewLogList: AppointmentLog[] = []; |
| | | reviewLogList: AppointmentLog[] = []; |
| | | unReviewLogList: AppointmentLog[] = []; |
| | | notificationList: NotificationList[] = []; |
| | | |
| | | get isUserLogin() { |
| | | return this.context.getters['localStorage/isUserLogin']; |
| | |
| | | } |
| | | |
| | | @Mutation |
| | | updateMyAppointmentReviewLog(data: AppointmentLog[]) { |
| | | this.myAppointmentReviewLogList = data; |
| | | updateReviewLog(data: AppointmentLog[]) { |
| | | this.reviewLogList = data; |
| | | } |
| | | |
| | | @Mutation |
| | | updateUnReviewLog(data: AppointmentLog[]) { |
| | | this.unReviewLogList = data; |
| | | } |
| | | |
| | | @Mutation |
| | | updateNotification(data: NotificationList[]) { |
| | | this.notificationList = data; |
| | | } |
| | | |
| | | @Action |
| | |
| | | } |
| | | }); |
| | | const sortedData = dataWithLatestDate.sort((a, b) => +b.compareDate - +a.compareDate); |
| | | this.context.commit('updateMyAppointmentReviewLog', sortedData); |
| | | const reviewLog = sortedData.filter(item => item.score); |
| | | const unReviewLog = sortedData.filter(item => !item.score); |
| | | this.context.commit('updateReviewLog', reviewLog); |
| | | this.context.commit('updateUnReviewLog', unReviewLog); |
| | | }); |
| | | } |
| | | |
| | |
| | | }); |
| | | } |
| | | |
| | | @Action |
| | | storeMyPersonalNotification() { |
| | | reviewsService.getMyPersonalNotification().then(data => { |
| | | this.context.commit('updateNotification', data); |
| | | }) |
| | | } |
| | | |
| | | } |