Merge branch 'master' of https://192.168.0.10:8443/r/pcalife/PAM
# Conflicts:
# PAMapp/components/QuickFilter/QuickFilterSelector.vue
# PAMapp/pages/quickFilter/index.vue
# PAMapp/shared/const/quickFilter-questionList.ts
刪除12個檔案
修改46個檔案
新增9個檔案
修改28個檔案名稱
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, Emit, Action, State, namespace } from 'nuxt-property-decorator'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | @Component |
| | |
| | | ? true : false |
| | | } |
| | | } |
| | | </script> |
| | | </script> |
| | |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | import { Vue, Component,} from 'vue-property-decorator'; |
| | | import * as _ from 'lodash'; |
| | | import { Role } from '~/assets/ts/models/enum/role'; |
| | | import { Role } from '~/shared/models/enum/role'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | @Component |
| | | export default class UiCarousel extends Vue { |
| | | @roleStorage.Getter currentRole!:string; |
| | | get label(): string { |
| | | |
| | | if (this.$route.name) { |
| | | const routeName = this.$route.name.split('-')[0]; |
| | | let featureLabel = ''; |
| | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, Action } from 'nuxt-property-decorator'; |
| | | |
| | | import appointmentService from '~/assets/ts/services/appointment.service'; |
| | | import { isMobileDevice } from '~/assets/ts/device'; |
| | | import { hideReviews } from '~/assets/ts/const/hide-reviews'; |
| | | import { ClientInfo } from '~/assets/ts/models/client.model'; |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import { isMobileDevice } from '~/shared/device'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | |
| | | |
| | | @Component({ |
| | |
| | | } |
| | | |
| | | markAppointment() { |
| | | appointmentService.markAsContact(this.client.id).then(data => { |
| | | myConsultantService.markAsContact(this.client.id).then(data => { |
| | | // TODO: è¦æ¥å¾å°å³åç updated client è³æ - Ben 2021/11/16 |
| | | const updatedClient = {...this.client}; |
| | | updatedClient.communicateStatus = 'contacted'; |
| | |
| | | <script lang='ts'> |
| | | import { Vue, Component, Prop } from 'nuxt-property-decorator'; |
| | | |
| | | import { ClientInfo } from '~/assets/ts/models/client.model'; |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | |
| | | @Component |
| | | export default class ClientList extends Vue { |
| | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, Action, namespace } from 'nuxt-property-decorator'; |
| | | |
| | | import appointmentService from '~/assets/ts/services/appointment.service'; |
| | | import { isMobileDevice } from '~/assets/ts/device'; |
| | | import { hideReviews } from '~/assets/ts/const/hide-reviews'; |
| | | import { UserReviewsConsultantsParams, userReviewsConsultants } from '~/assets/ts/api/consultant'; |
| | | import { Consultant, ConsultantWithAppointmentId } from '~/assets/ts/models/consultant.model'; |
| | | import { Appointment } from '~/assets/ts/models/appointment.model'; |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import reviewsService from '~/shared/services/reviews.service'; |
| | | import { isMobileDevice } from '~/shared/device'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | import { Consultant, ConsultantWithAppointmentId } from '~/shared/models/consultant.model'; |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | import { UserReviewsConsultantsParams } from '~/shared/models/reviews.model'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | @Component({ |
| | |
| | | } |
| | | this.appointmentDetail.satisfactionScore = this.inputScore; |
| | | |
| | | userReviewsConsultants(reviewParams).then((res) => { |
| | | reviewsService.userReviewsConsultants(reviewParams).then((res) => { |
| | | this.reviewsBtn = false; |
| | | this.storeConsultantList(); |
| | | }); |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, namespace } from 'nuxt-property-decorator'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop } from 'vue-property-decorator'; |
| | | import { SwiperOptions } from 'swiper'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { hideReviews } from '~/assets/ts/const/hide-reviews'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | |
| | | @Component |
| | | export default class UiSwiper extends Vue { |
| | |
| | | @Prop() agents!: Consultant[]; |
| | | |
| | | hideReviews = hideReviews ; |
| | | |
| | | |
| | | swiperOptions: SwiperOptions = { |
| | | loop: false, |
| | | slideToClickedSlide: false, |
| | |
| | | <script lang="ts"> |
| | | import { Vue, Component } from 'vue-property-decorator'; |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | import { Role } from '~/assets/ts/models/enum/role'; |
| | | import { Role } from '~/shared/models/enum/role'; |
| | | import * as _ from 'lodash'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | |
| | | <script lang="ts"> |
| | | import { ElCarousel } from 'element-ui/types/carousel'; |
| | | import { Vue, Component, Prop } from 'vue-property-decorator'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { hideReviews } from '~/assets/ts/const/hide-reviews'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | |
| | | @Component |
| | | export default class QuickFilterConsultantList extends Vue { |
| | |
| | | } |
| | | } |
| | | |
| | | </style> |
| | | </style> |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, Watch, Emit } from 'nuxt-property-decorator'; |
| | | import { hideReviews } from '~/assets/ts/const/hide-reviews'; |
| | | import { FastQueryParams, QuestionOption, Selected } from '~/assets/ts/models/quickFilter.model'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | import { FastQueryParams, QuestionOption, Selected } from '~/shared/models/quick-filter.model'; |
| | | @Component |
| | | export default class QuickFilterDrawer extends Vue { |
| | | pickedItem: FastQueryParams = { |
| | |
| | | } |
| | | } |
| | | |
| | | </style> |
| | | </style> |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop } from 'vue-property-decorator'; |
| | | import { isMobileDevice } from '~/assets/ts/device'; |
| | | import { isMobileDevice } from '~/shared/device'; |
| | | |
| | | @Component |
| | | export default class UiField extends Vue { |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, Emit, Watch } from 'nuxt-property-decorator'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | |
| | | @Component |
| | | export default class UiPagination extends Vue { |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop,Emit, Watch, PropSync} from 'vue-property-decorator'; |
| | | import { isMobileDevice } from '../assets/ts/device'; |
| | | import { isMobileDevice } from '~/shared/device'; |
| | | @Component |
| | | export default class PopUpFrame extends Vue { |
| | | @PropSync('isOpen',{type:Boolean,default:false}) syncIsOpen!:boolean; |
| | |
| | | private get isUseDialog() : boolean { |
| | | return this.syncIsOpen && !isMobileDevice(); |
| | | } |
| | | |
| | | |
| | | private set isUseDialog(value: boolean) { |
| | | this.$emit('update:isOpen',value); |
| | | } |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue,Component } from 'vue-property-decorator' |
| | | import { getUserAccountSetting, updateAccountSetting } from '~/assets/ts/api/consultant'; |
| | | import { UserSetting } from '~/assets/ts/models/account.model'; |
| | | import { UserSetting } from '~/shared/models/account.model'; |
| | | |
| | | import accountSettingService from '~/shared/services/account-setting.service'; |
| | | |
| | | @Component |
| | | export default class AccountSetting extends Vue { |
| | |
| | | phone: this.phoneValue, |
| | | email: this.emailValue |
| | | } |
| | | updateAccountSetting(editSettingInfo).then((res: any) => { |
| | | accountSettingService.updateAccountSetting(editSettingInfo).then((res: any) => { |
| | | console.log('updateRes:', res); |
| | | this.resetSettingForm(); |
| | | }); |
| | |
| | | } |
| | | |
| | | mounted(){ |
| | | getUserAccountSetting().then((userInfo: UserSetting)=>{ |
| | | accountSettingService.getUserAccountSetting().then((userInfo: UserSetting)=>{ |
| | | this._userSetting = { |
| | | name: userInfo.name || '', |
| | | phone: userInfo.phone || '', |
| | |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | import { Vue, Component } from 'vue-property-decorator'; |
| | | |
| | | import myConsultantService from '~/assets/ts/services/my-consultant.service'; |
| | | import { AgentInfo } from '~/assets/ts/models/agent-info.model'; |
| | | import { hideReviews } from '~/assets/ts/const/hide-reviews'; |
| | | import { Role } from '~/assets/ts/models/enum/role'; |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | import { AgentInfo } from '~/shared/models/agent-info.model'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | import { Role } from '~/shared/models/enum/role'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | |
| | | <script lang="ts"> |
| | | import { Vue, Component , namespace } from 'nuxt-property-decorator'; |
| | | import { AxiosError } from 'axios'; |
| | | import { Role } from '~/assets/ts/models/enum/role'; |
| | | import ErrorMessageBox from '~/assets/ts/errorService'; |
| | | import loginService from '~/assets/ts/services/login.service' |
| | | import { Role } from '~/shared/models/enum/role'; |
| | | import ErrorMessageBox from '~/shared/errorService'; |
| | | import loginService from '~/shared/services/login.service' |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | @Component({ |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component } from 'nuxt-property-decorator'; |
| | | import { faqList } from '~/assets/ts/const/faqList'; |
| | | import { faqList } from '~/shared/const/faqList'; |
| | | |
| | | @Component |
| | | export default class Faq extends Vue { |
| | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped></style> |
| | | <style lang="scss" scoped></style> |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, State, Action, Watch, namespace } from 'nuxt-property-decorator'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | @Component({ |
| | |
| | | <script lang="ts"> |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | import { Vue, Component, Ref } from 'vue-property-decorator'; |
| | | import ErrorMessageBox from '~/assets/ts/errorService'; |
| | | import { OtpErrorCode } from '~/assets/ts/models/enum/otpErrorCode'; |
| | | import { Role } from '~/assets/ts/models/enum/role'; |
| | | import { LoginRequest } from '~/assets/ts/models/loginRequest.model'; |
| | | import { LoginVerify } from '~/assets/ts/models/loginVerify.model'; |
| | | import { OtpInfo } from '~/assets/ts/models/otpInfo.model'; |
| | | import { RegisterInfo } from '~/assets/ts/models/registerInfo'; |
| | | import loginService from '~/assets/ts/services/login.service'; |
| | | import ErrorMessageBox from '~/shared/errorService'; |
| | | import { OtpErrorCode } from '~/shared/models/enum/otpErrorCode'; |
| | | import { Role } from '~/shared/models/enum/role'; |
| | | import { LoginRequest } from '~/shared/models/loginRequest.model'; |
| | | import { LoginVerify } from '~/shared/models/loginVerify.model'; |
| | | import { OtpInfo } from '~/shared/models/otpInfo.model'; |
| | | import { RegisterInfo } from '~/shared/models/registerInfo'; |
| | | import loginService from '~/shared/services/login.service'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | |
| | | |
| | | import * as _ from 'lodash'; |
| | | |
| | | import { ClientInfo } from '~/assets/ts/models/client.model'; |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | |
| | | @Component({ |
| | | layout: 'home', |
| | |
| | | <script lang="ts"> |
| | | import { Vue, Component, State, Watch } from 'nuxt-property-decorator'; |
| | | |
| | | import { ClientInfo } from '~/assets/ts/models/client.model'; |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | |
| | | @Component |
| | | export default class ClientReservedList extends Vue { |
| | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Watch, State } from 'nuxt-property-decorator'; |
| | | |
| | | import { ClientInfo } from '~/assets/ts/models/client.model'; |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | |
| | | @Component |
| | | export default class ClientContactedList extends Vue { |
| | |
| | | |
| | | <script lang='ts'> |
| | | import { Vue, Component, Watch, State, Action } from 'nuxt-property-decorator'; |
| | | import { Consultant, ConsultantWithAppointmentId } from '~/assets/ts/models/consultant.model'; |
| | | import { Consultant, ConsultantWithAppointmentId } from '~/shared/models/consultant.model'; |
| | | |
| | | @Component |
| | | export default class myConsultantList extends Vue { |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop } from 'nuxt-property-decorator'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | |
| | | |
| | | @Component |
| | |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | </script> |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop } from 'nuxt-property-decorator' ; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | |
| | | |
| | | @Component |
| | |
| | | this.pageList = pageList; |
| | | } |
| | | } |
| | | </script> |
| | | </script> |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, State, Action, Watch, namespace } from 'nuxt-property-decorator'; |
| | | import { addFavoriteConsultant, appointmentDemand, AppointmentParams, AppointmentRequests ,editAppointment } from '~/assets/ts/api/consultant'; |
| | | import { getRequestQuestionFromStorage, getRequestsFromStorage, removeRequestQuestionFromStorage, setRequestsToStorage } from '~/assets/ts/storageRequests'; |
| | | import { getRequestsFromStorage, removeRequestQuestionFromStorage, setRequestsToStorage } from '~/shared/storageRequests'; |
| | | import _ from 'lodash'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { ContactType } from '~/assets/ts/models/enum/ContactType'; |
| | | import { Gender } from '~/assets/ts/models/enum/Gender'; |
| | | import { RegisterInfo } from '~/assets/ts/models/registerInfo'; |
| | | |
| | | import queryConsultantService from '~/shared/services/query-consultant.service'; |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | import { ContactType } from '~/shared/models/enum/ContactType'; |
| | | import { Gender } from '~/shared/models/enum/Gender'; |
| | | import { RegisterInfo } from '~/shared/models/registerInfo'; |
| | | import { AppointmentParams, AppointmentRequests } from '~/shared/models/appointment.model'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | @Component |
| | |
| | | if (this.isEditBtn) { |
| | | this.sentEditAppointmentDemand(); |
| | | } else { |
| | | addFavoriteConsultant([this.$route.params.agentNo]).then(res => this.sentAppointmentDemand()); |
| | | queryConsultantService.addFavoriteConsultant([this.$route.params.agentNo]).then(res => this.sentAppointmentDemand()); |
| | | } |
| | | |
| | | } |
| | |
| | | agentNo: this.$route.params.agentNo |
| | | }; |
| | | |
| | | appointmentDemand(data).then(res => { |
| | | queryConsultantService.appointmentDemand(data).then(res => { |
| | | this.sendReserve = true; |
| | | this.myRequest.hopeContactTime = []; |
| | | setRequestsToStorage(this.myRequest); |
| | |
| | | id: this.appointmentId, |
| | | otherRequirement: null |
| | | } |
| | | editAppointment(info).then(res => { |
| | | appointmentService.editAppointment(info).then(res => { |
| | | this.sendReserve = true; |
| | | this.myRequest.hopeContactTime = []; |
| | | setRequestsToStorage(this.myRequest); |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, namespace } from 'nuxt-property-decorator'; |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { questionList } from '~/assets/ts/const/quickFilter-questionList'; |
| | | import { FastQueryParams, QuestionOption, Selected } from '~/assets/ts/models/quickFilter.model'; |
| | | import { fastQuery } from '~/assets/ts/api/consultant'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | import { FastQueryParams, QuestionOption, Selected } from '~/shared/models/quick-filter.model'; |
| | | import queryConsultantService from '~/shared/services/query-consultant.service'; |
| | | import { questionList } from '~/shared/const/quickFilter-questionList'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | @Component({ |
| | |
| | | seniority: this.getSeniority() |
| | | } |
| | | |
| | | fastQuery(data).then((consultantList) => { |
| | | queryConsultantService.fastQuery(data).then((consultantList) => { |
| | | this.consultantList = consultantList; |
| | | this.storageQuickFilter(JSON.stringify(this.confirmItem)) |
| | | }) |
| | |
| | | } |
| | | } |
| | | |
| | | </style> |
| | | </style> |
| | |
| | | State |
| | | } from 'nuxt-property-decorator'; |
| | | import * as _ from 'lodash'; |
| | | import { Seniority } from '~/assets/ts/models/enum/seniority'; |
| | | import { Seniority } from '~/shared/models/enum/seniority'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | |
| | |
| | | </template> |
| | | <script lang="ts"> |
| | | import {Vue,Component, State, namespace, Action} from 'nuxt-property-decorator'; |
| | | import { AgentOfStrictQuery } from '~/assets/ts/api/consultant'; |
| | | import { hideReviews } from '~/assets/ts/const/hide-reviews'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | import { AgentOfStrictQuery } from '~/shared/models/strict-query.model'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | |
| | |
| | | </section> |
| | | |
| | | <section class="user-reviews-content"> |
| | | <div |
| | | <div |
| | | class="user-reviews-card" |
| | | v-for="(appointmentLog, index) in myAppointmentReviewLogList" |
| | | :key="index"> |
| | |
| | | </div> |
| | | <div class="user-reviews-card-date"> |
| | | <div class="date"> |
| | | <UiDateFormat |
| | | <UiDateFormat |
| | | :date="appointmentLog.lastModifiedDate" |
| | | onlyShowSection="DAY" /> |
| | | </div> |
| | | <div class="time"> |
| | | <UiDateFormat |
| | | <UiDateFormat |
| | | :date="appointmentLog.lastModifiedDate" |
| | | onlyShowSection="TIME" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | |
| | | |
| | | </div> |
| | | |
| | | </template> |
| | | <script lang="ts"> |
| | | import { Vue, Component, Action, State, namespace } from 'nuxt-property-decorator'; |
| | | import { AppointmentLog } from '~/assets/ts/models/appointment.model'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | |
| | | width:52px; |
| | | .date{ |
| | | margin-bottom: 2px; |
| | | |
| | | |
| | | } |
| | | } |
| | | } |
| | |
| | | flex: 1; |
| | | } |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | <template> |
| | | <template> |
| | | <div class="reviews-page"> |
| | | <!-- 顧客ç¼é滿æåº¦çµ¦é¡§å --> |
| | | <div class="reviews-banner"></div> |
| | | |
| | | <section class="reviews-container"> |
| | | <section class="reviews-container"> |
| | | <section class="reviews-header"> |
| | | <div class="reviews-header-container"> |
| | | <div class="reviews-header-title">滿æåº¦èª¿æ¥</div> |
| | |
| | | <div class="card-txt"> |
| | | å°æ¼é¡§å |
| | | <span class="p">{{item.name}}</span>çæ´é«æå,æ¨çµ¦äºå¹¾é¡æçè©å¹? |
| | | <div |
| | | <div |
| | | class="card-score" |
| | | v-if="!isMobileDevice"> |
| | | <el-rate class="user-reviews-rate" v-model="item.avgScore"></el-rate> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div |
| | | <div |
| | | class="card-score" |
| | | v-if="isMobileDevice"> |
| | | <el-rate |
| | | <el-rate |
| | | class="user-reviews-rate" |
| | | v-model="item.avgScore"></el-rate> |
| | | </div> |
| | |
| | | <el-button type="primary" class="reviews-dialog-btn" @click.native="reviewsDialogCheck">æç¥éäº</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | |
| | | |
| | | </div> |
| | | </template> |
| | | <script lang="ts"> |
| | | import { Vue,Component } from 'vue-property-decorator' |
| | | import { isMobileDevice } from '~/assets/ts/device'; |
| | | import { isMobileDevice } from '~/shared/device'; |
| | | |
| | | |
| | | @Component({ |
| | |
| | | |
| | | } |
| | | |
| | | </style> |
| | | </style> |
| | |
| | | </section> |
| | | |
| | | <section class="user-reviews-content"> |
| | | <div |
| | | <div |
| | | class="user-reviews-card" |
| | | v-for="(appointmentLog, index) in myAppointmentReviewLogList" |
| | | :key="index"> |
| | |
| | | </div> |
| | | <div class="user-reviews-card-date"> |
| | | <div class="date"> |
| | | <UiDateFormat |
| | | <UiDateFormat |
| | | :date="appointmentLog.lastModifiedDate" |
| | | onlyShowSection="DAY" /> |
| | | </div> |
| | | <div class="time"> |
| | | <UiDateFormat |
| | | <UiDateFormat |
| | | :date="appointmentLog.lastModifiedDate" |
| | | onlyShowSection="TIME" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | |
| | | |
| | | </div> |
| | | |
| | | </template> |
| | | <script lang="ts"> |
| | | import { Vue, Component, Action, State, namespace } from 'nuxt-property-decorator'; |
| | | import { AppointmentLog } from '~/assets/ts/models/appointment.model'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | |
| | | mounted() { |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | |
| | | |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | |
| | | width:52px; |
| | | .date{ |
| | | margin-bottom: 2px; |
| | | |
| | | |
| | | } |
| | | } |
| | | } |
| | |
| | | flex: 1; |
| | | } |
| | | } |
| | | </style> |
| | | </style> |
File was renamed from PAMapp/assets/ts/const/quickFilter-questionList.ts |
| | |
| | | import { Seniority } from "../models/enum/seniority"; |
| | | import { QuestionOption } from "../models/quickFilter.model"; |
| | | import { QuestionOption } from "../models/quick-filter.model"; |
| | | |
| | | |
| | | export const questionList: QuestionOption[] = [ |
| | | { |
File was renamed from PAMapp/assets/ts/models/appointment.model.ts |
| | |
| | | customerId : number; |
| | | name : string; |
| | | } |
| | | |
| | | export interface AppointmentParams { |
| | | phone : string; |
| | | email : string; |
| | | contactType : string; |
| | | gender : string; |
| | | age : string; |
| | | job : string; |
| | | requirement : string; |
| | | hopeContactTime: string; |
| | | agentNo : string; |
| | | } |
| | | export interface EditAppointmentParams { |
| | | id : number, |
| | | phone : string, |
| | | email : string, |
| | | contactType : string, |
| | | gender : string, |
| | | age : string, |
| | | job : string, |
| | | requirement : string, |
| | | hopeContactTime : string, |
| | | otherRequirement: null |
| | | } |
| | | export interface AppointmentRequests { |
| | | phone : string, |
| | | email : string, |
| | | contactType : string, |
| | | gender : string, |
| | | age : string, |
| | | job : string, |
| | | requirement : string[], |
| | | hopeContactTime: ContactTime[], |
| | | agentNo : string, |
| | | } |
| | | export interface ContactTime { |
| | | selectWeekOptions : string[], |
| | | selectTimesOptions: string[] |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | export enum Role{ |
| | | ADMIN = 'admin', |
| | | USER = 'user', |
| | | NOT_LOGIN = '' |
| | | } |
File was renamed from PAMapp/assets/ts/models/quickFilter.model.ts |
| | |
| | | export interface QuestionOption { |
| | | title: string; |
| | | title : string; |
| | | detail: Detail[]; |
| | | type: string; |
| | | name: string; |
| | | type : string; |
| | | name : string; |
| | | } |
| | | |
| | | interface Detail { |
| | | value: string; |
| | | name?: string; |
| | | value : string; |
| | | name? : string; |
| | | className: string; |
| | | subTitle?: string |
| | | } |
| | |
| | | |
| | | export interface Selected { |
| | | option: string; |
| | | value: any; |
| | | } |
| | | value : any; |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | export interface UserReviewsConsultantsParams{ |
| | | appointmentId: number, |
| | | score : number, |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | |
| | | export interface StrictQueryParams { |
| | | gender : string; |
| | | avgScore : number; |
| | | status : string; //phase 1 disable |
| | | area : string; |
| | | requirements : string[]; |
| | | otherRequirement: string; |
| | | seniority : string; |
| | | popularTags : string[]; |
| | | otherPopularTags: string; |
| | | } |
| | | |
| | | export interface AgentOfStrictQuery { |
| | | agentNo : string; |
| | | name : string; |
| | | img : string; |
| | | expertise : string[]; |
| | | avgScore : number; |
| | | contactStatus: null; |
| | | updateTime : null; |
| | | seniority : string; |
| | | new : boolean; |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { UserSetting } from "~/shared/models/account.model"; |
| | | |
| | | class AccountSettingService{ |
| | | |
| | | //åå¾ä½¿ç¨è
帳èè³è¨ |
| | | async getUserAccountSetting() : Promise<UserSetting> { |
| | | return http.get<UserSetting>('/customer/info').then(res => res.data); |
| | | } |
| | | //æ´æ°ä½¿ç¨è
帳èè³è¨ |
| | | async updateAccountSetting(params: any) : Promise<any> { |
| | | return http.put('/customer/info', params ).then(res => res.data); |
| | | } |
| | | |
| | | } |
| | | export default new AccountSettingService(); |
File was renamed from PAMapp/assets/ts/services/appointment.service.ts |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { ClientInfo } from "../models/client.model"; |
| | | import { AppointmentDetail } from "../models/appointment.model"; |
| | | import { ClientInfo } from "~/shared/models/client.model"; |
| | | import { AppointmentDetail, EditAppointmentParams } from "~/shared/models/appointment.model"; |
| | | |
| | | class AppointmentService { |
| | | |
| | |
| | | }); |
| | | } |
| | | |
| | | // é¡§åç»å
¥é¡¯ç¤ºæ°é ç´å®çæ¸å¾è§¸ç¼ |
| | | private viewAllAppointment(): void { |
| | | http.post('/consultant/record/allAppointmentsView').then(); |
| | | } |
| | | |
| | | // æ¨è¨çºå·²è¯çµ¡ |
| | | markAsContact(appointmentId: number): Promise<void> { |
| | | // TODO: è·å¾ç«¯ç¢ºèªï¼é裡ç API ä¸æè©²å³å void, èæ¯æè©²æ¯æ´æ°å¾çè³æ - Ben 2021/11/16 |
| | | // return http.post('/appointment/markAsContacted/'+appointmentId, undefined, {headers}) |
| | | // .then(res => res.data) |
| | | return http.post(`/appointment/markAsContacted/${appointmentId}`); |
| | | } |
| | | |
| | | // è®åé ç´å®æè§¸ç¼ï¼ç´éè®åé ç´å®æé |
| | |
| | | return http.delete(`/appointment/${appointmentId}`); |
| | | } |
| | | |
| | | // 編輯é ç´ |
| | | editAppointment(editAppointmentParams: EditAppointmentParams) { |
| | | return http.put('/appointment', editAppointmentParams); |
| | | } |
| | | |
| | | } |
| | | |
| | | export default new AppointmentService(); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { AgentInfo } from '~/shared/models/agent-info.model'; |
| | | import { Consultant } from "../models/consultant.model"; |
| | | |
| | | class MyConsultantService { |
| | | |
| | | async getFavoriteConsultantList(): Promise<Consultant[]> { |
| | | return http.get<Consultant[]>('/consultant/favorite').then((res) => { |
| | | const hasNewConsultant = res.data.find((consultant) => !consultant.customerViewTime); |
| | | if (hasNewConsultant) { |
| | | this.viewMyConsultantList(); |
| | | }; |
| | | return res.data; |
| | | }); |
| | | } |
| | | |
| | | private viewMyConsultantList(): void { |
| | | http.post('/consultant/favorite/view'); |
| | | } |
| | | |
| | | //é¡§å詳細è³è¨ |
| | | async getConsultantDetail(agentNo:string): Promise<AgentInfo> { |
| | | return http.get('/consultant/detail', {params:{agentNo:agentNo}}).then((res) => res.data); |
| | | } |
| | | |
| | | // ç§»é¤é¡§å |
| | | async deleteConsultant(agentId: string) { |
| | | return http.delete(`/consultant/favorite/${agentId}`); |
| | | } |
| | | |
| | | // æ¨è¨çºå·²è¯çµ¡ |
| | | markAsContact(appointmentId: number): Promise<void> { |
| | | // TODO: è·å¾ç«¯ç¢ºèªï¼é裡ç API ä¸æè©²å³å void, èæ¯æè©²æ¯æ´æ°å¾çè³æ - Ben 2021/11/16 |
| | | // return http.post('/appointment/markAsContacted/'+appointmentId, undefined, {headers}) |
| | | // .then(res => res.data) |
| | | return http.post(`/appointment/markAsContacted/${appointmentId}`); |
| | | } |
| | | |
| | | } |
| | | |
| | | export default new MyConsultantService(); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { Consultant } from "~/shared/models/consultant.model"; |
| | | import { FastQueryParams } from "~/shared/models/quick-filter.model"; |
| | | import { AgentOfStrictQuery, StrictQueryParams } from "~/shared/models/strict-query.model"; |
| | | import { AppointmentParams } from "~/shared/models/appointment.model"; |
| | | |
| | | class QueryConsultantService { |
| | | |
| | | // æ¨è¦ä¿éªé¡§å |
| | | async getRecommendConsultantList(): Promise<Consultant[]> { |
| | | return http.get<Consultant[]>('/consultant/recommend').then((res) => res.data); |
| | | } |
| | | |
| | | // å¿«éç¯©é¸ |
| | | async fastQuery(data: FastQueryParams): Promise<Consultant[]> { |
| | | return http.post<Consultant[]>('/consultant/fastQuery', data).then(res => res.data); |
| | | } |
| | | |
| | | // å´é¸é
å° |
| | | async strictQuery(data:StrictQueryParams): Promise<AgentOfStrictQuery[]>{ |
| | | return http.post('/consultant/strictQuery', data).then((res) => res.data); |
| | | } |
| | | |
| | | // å å
¥é¡§å |
| | | async addFavoriteConsultant(agentNoList: string[]) { |
| | | return http.post('/consultant/favorite', { agentNoList }); |
| | | } |
| | | |
| | | // é ç´åè©¢å |
| | | async appointmentDemand(data: AppointmentParams) { |
| | | return http.post('/appointment/customer/create', data); |
| | | } |
| | | |
| | | } |
| | | |
| | | export default new QueryConsultantService(); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { UserReviewsConsultantsParams } from "../models/reviews.model"; |
| | | import { AppointmentLog } from "../models/appointment.model"; |
| | | |
| | | class ReviewsService { |
| | | |
| | | //客æ¶é²è¡æ»¿æåº¦è©å |
| | | userReviewsConsultants(data: UserReviewsConsultantsParams) { |
| | | return http.post('/satisfaction/create', data ); |
| | | } |
| | | //å徿æè©åç´é |
| | | async getMyReviewLog(): Promise<AppointmentLog[]> { |
| | | return http.get('/satisfaction/getMySatisfaction').then(res => res.data); |
| | | } |
| | | } |
| | | |
| | | export default new ReviewsService(); |
File was renamed from PAMapp/assets/ts/storageRequests.ts |
| | |
| | | import { AppointmentRequests } from "./api/consultant"; |
| | | import { AppointmentRequests } from "./models/appointment.model"; |
| | | |
| | | export function getRequestsFromStorage(): AppointmentRequests { |
| | | const requests = localStorage.getItem('myRequests'); |
| | |
| | | import { StrictQueryParams } from '~/shared/models/strict-query.model'; |
| | | import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators' |
| | | |
| | | import { getMyReviewLog } from '~/assets/ts/api/appointment'; |
| | | import { recommend, AgentOfStrictQuery, addFavoriteConsultant, deleteConsultant, strictQuery } from '~/assets/ts/api/consultant'; |
| | | import { getFavoriteFromStorage, setFavoriteToStorage } from '~/assets/ts/storageConsultant'; |
| | | import { getFavoriteFromStorage, setFavoriteToStorage } from '~/shared/storageConsultant'; |
| | | |
| | | import myConsultantService from '~/assets/ts/services/my-consultant.service'; |
| | | import appointmentService from '~/assets/ts/services/appointment.service'; |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | import queryConsultantService from '~/shared/services/query-consultant.service'; |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import reviewsService from '~/shared/services/reviews.service'; |
| | | |
| | | import { Consultant } from '~/assets/ts/models/consultant.model'; |
| | | import { AppointmentLog } from '~/assets/ts/models/appointment.model'; |
| | | import { ClientInfo } from '~/assets/ts/models/client.model'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | import { AgentOfStrictQuery } from '~/shared/models/strict-query.model'; |
| | | @Module |
| | | export default class Store extends VuexModule { |
| | | recommendList: Consultant[] = []; |
| | |
| | | |
| | | @Action |
| | | storeRecommendList() { |
| | | recommend().then(data => { |
| | | queryConsultantService.getRecommendConsultantList().then(data => { |
| | | this.context.commit('updateRecommend', data) |
| | | }) |
| | | } |
| | |
| | | |
| | | if (localData?.length) { |
| | | const agentNoList = localData.map(i => i.agentNo) |
| | | await addFavoriteConsultant(agentNoList).then(res => { |
| | | await queryConsultantService.addFavoriteConsultant(agentNoList).then(res => { |
| | | localStorage.removeItem('favoriteConsultant') |
| | | }) |
| | | } |
| | |
| | | if (!this.isUserLogin) { |
| | | setFavoriteToStorage(left); |
| | | } else { |
| | | await deleteConsultant(agentNo) |
| | | await myConsultantService.deleteConsultant(agentNo) |
| | | } |
| | | |
| | | this.context.commit('updateConsultantList', left) |
| | |
| | | if (!found) { |
| | | const newData = [consultantToAdd].concat(this.myConsultantList); |
| | | if (this.isUserLogin) { |
| | | await addFavoriteConsultant([consultantToAdd.agentNo]) |
| | | await queryConsultantService.addFavoriteConsultant([consultantToAdd.agentNo]) |
| | | } else { |
| | | setFavoriteToStorage(newData); |
| | | } |
| | |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog() { |
| | | getMyReviewLog().then((data) => { |
| | | reviewsService.getMyReviewLog().then((data) => { |
| | | const dataWithLatestDate = data.map((item) => { |
| | | return { |
| | | ...item, |
| | |
| | | } |
| | | |
| | | @Action |
| | | async storeStrictQueryList(strictQueryDto) { |
| | | return await strictQuery(strictQueryDto).then(res=>{ |
| | | async storeStrictQueryList(strictQueryDto: StrictQueryParams) { |
| | | return await queryConsultantService.strictQuery(strictQueryDto).then(res=>{ |
| | | this.context.commit('localStorage/storageRecommendConsultant', JSON.stringify(strictQueryDto)); |
| | | this.context.commit('updateStrictQueryList', res.data) |
| | | return res.data.length; |
| | | this.context.commit('updateStrictQueryList', res) |
| | | return res.length; |
| | | }); |
| | | } |
| | | |
| | |
| | | import { Module, Mutation, VuexModule ,Action } from 'vuex-module-decorators'; |
| | | import { Role } from '~/assets/ts/models/enum/Role'; |
| | | import { Selected } from '~/assets/ts/models/quickFilter.model'; |
| | | import { Role } from '~/shared/models/enum/role'; |
| | | import { Selected } from '~/shared/models/quick-filter.model'; |
| | | @Module |
| | | export default class LocalStorage extends VuexModule { |
| | | id_token = localStorage.getItem('id_token'); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | ALTER TABLE omo.consultant ADD email varchar NULL; |
| | |
| | | private String eServiceLoginUrl; |
| | | private String eServiceLoginFunc; |
| | | private String eServiceLoginSys; |
| | | private String frontEndDomain; |
| | | private boolean sendNotifyMsg; |
| | | private SMS sms; |
| | | private Email email; |
| | | |
| | |
| | | |
| | | public void seteServiceLoginSys(String eServiceLoginSys) { |
| | | this.eServiceLoginSys = eServiceLoginSys; |
| | | } |
| | | |
| | | public String getFrontEndDomain() { |
| | | return frontEndDomain; |
| | | } |
| | | |
| | | public void setFrontEndDomain(String frontEndDomain) { |
| | | this.frontEndDomain = frontEndDomain; |
| | | } |
| | | |
| | | public boolean isSendNotifyMsg() { |
| | | return sendNotifyMsg; |
| | | } |
| | | |
| | | public void setSendNotifyMsg(boolean sendNotifyMsg) { |
| | | this.sendNotifyMsg = sendNotifyMsg; |
| | | } |
| | | |
| | | public SMS getSms() { |
| | |
| | | public static class Email { |
| | | private String url; |
| | | private String functionId; |
| | | private String senderEmail; |
| | | |
| | | public String getUrl() { |
| | | return url; |
| | |
| | | public void setFunctionId(String functionId) { |
| | | this.functionId = functionId; |
| | | } |
| | | |
| | | public String getSenderEmail() { |
| | | return senderEmail; |
| | | } |
| | | |
| | | public void setSenderEmail(String senderEmail) { |
| | | this.senderEmail = senderEmail; |
| | | } |
| | | } |
| | | } |
| | |
| | | @Column(name = "communication_style") |
| | | private String communicationStyle; |
| | | |
| | | @Column(name = "email") |
| | | private String email; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | |
| | | this.communicationStyle = communicationStyle; |
| | | } |
| | | |
| | | public String getEmail() { |
| | | return email; |
| | | } |
| | | |
| | | public void setEmail(String email) { |
| | | this.email = email; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "Consultant{" + |
| | |
| | | ", gender=" + gender + |
| | | ", phoneNumber='" + phoneNumber + '\'' + |
| | | ", companyAddress='" + companyAddress + '\'' + |
| | | ", seniorityYear='" + seniorityYear + '\'' + |
| | | ", seniorityMonth='" + seniorityMonth + '\'' + |
| | | ", seniorityYear=" + seniorityYear + |
| | | ", seniorityMonth=" + seniorityMonth + |
| | | ", concept='" + concept + '\'' + |
| | | ", experience='" + experience + '\'' + |
| | | ", award='" + award + '\'' + |
| | | ", recommend=" + recommend + |
| | | ", communicationStyle='" + communicationStyle + '\'' + |
| | | ", email='" + email + '\'' + |
| | | '}'; |
| | | } |
| | | } |
| | |
| | | @Autowired |
| | | SatisfactionService satisfactionService; |
| | | |
| | | public void customerCreateAppointment(AppointmentCreateDTO appointmentCreateDTO) { |
| | | public Appointment customerCreateAppointment(AppointmentCreateDTO appointmentCreateDTO) { |
| | | Appointment appointment = appointmentDTOMapper.toAppointment(appointmentCreateDTO); |
| | | appointment.setStatus(AVAILABLE); |
| | | appointment.setCustomerId(SecurityUtils.getCustomerDBId()); |
| | | appointment.setCommunicateStatus(ContactStatusEnum.RESERVED); |
| | | appointmentRepository.save(appointment); |
| | | return appointmentRepository.save(appointment); |
| | | } |
| | | |
| | | public void updateAppointment(AppointmentUpdateDTO updateAppointmentDTO) { |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.pollex.pam.config.ApplicationProperties; |
| | | import com.pollex.pam.config.ApplicationProperties.SMS; |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.repository.ConsultantRepository; |
| | | import com.pollex.pam.service.dto.*; |
| | | import com.pollex.pam.service.util.HttpRequestUtil; |
| | | import com.pollex.pam.web.rest.errors.SendEmailFailException; |
| | | import com.pollex.pam.web.rest.errors.SendSMSFailException; |
| | | import io.jsonwebtoken.lang.Assert; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | import org.thymeleaf.context.Context; |
| | | import org.thymeleaf.spring5.SpringTemplateEngine; |
| | | |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.time.LocalDateTime; |
| | |
| | | private static final Logger log = LoggerFactory.getLogger(SendMsgService.class); |
| | | private final Encoder encoder = Base64.getEncoder(); |
| | | |
| | | private static final String EMAIL_SUBJECT = "ä¿èª åªåå¹³å°ç³»çµ±éç¥ï¼æ°é ç´å®"; |
| | | |
| | | @Autowired |
| | | ApplicationProperties applicationProperties; |
| | | |
| | | public void sendMsgBySMS(String toMobile, String content) throws SendSMSFailException{ |
| | | @Autowired |
| | | ConsultantRepository consultantRepository; |
| | | |
| | | @Autowired |
| | | SpringTemplateEngine springTemplateEngine; |
| | | |
| | | public void sendAppointmentNotify(Appointment appointment) { |
| | | Assert.notNull(appointment); |
| | | |
| | | log.debug("is need sending notify msg = {}", applicationProperties.isSendNotifyMsg()); |
| | | if(applicationProperties.isSendNotifyMsg()) { |
| | | log.debug("sending appointment notify, appointmentId = {}", appointment.getId()); |
| | | sendAppointmentNotifyBySMS(appointment); |
| | | sendAppointmentNotifyByEmail(appointment); |
| | | |
| | | // todo é確èªä¿èª æ¯å¦æéæ±ä½¿ç¨html mail |
| | | // sendAppointmentNotifyByHtmlEmail(appointment); |
| | | } |
| | | } |
| | | |
| | | private void sendAppointmentNotifyBySMS(Appointment appointment) throws SendSMSFailException { |
| | | String msg = getAppointmentNotifyWording(appointment); |
| | | String consultantMobile = consultantRepository.findOneByAgentNo(appointment.getAgentNo()).get().getPhoneNumber(); |
| | | |
| | | try { |
| | | sendMsgBySMS(consultantMobile, msg); |
| | | } catch (SendSMSFailException e) { |
| | | log.debug("send sms failed, appointment Id = {}", appointment.getId(), e); |
| | | } |
| | | } |
| | | |
| | | private void sendAppointmentNotifyByEmail(Appointment appointment) { |
| | | // todo éå¾ç¥ä¿èª 系統å¯ä»¶ä¿¡ç®± (ä¸¦æ¹æ¼è¨å®æªä¸) |
| | | String senderEmail = applicationProperties.getEmail().getSenderEmail(); |
| | | String consultantEmail = consultantRepository.findOneByAgentNo(appointment.getAgentNo()).get().getEmail(); |
| | | String content = getAppointmentNotifyWording(appointment); |
| | | |
| | | try { |
| | | sendMsgByEmail(senderEmail, consultantEmail, EMAIL_SUBJECT, content, false); |
| | | } catch (SendEmailFailException e) { |
| | | log.debug("send email failed, appointment Id = {}", appointment.getId(), e); |
| | | } |
| | | } |
| | | |
| | | private void sendAppointmentNotifyByHtmlEmail(Appointment appointment) { |
| | | // todo éå¾ç¥ä¿èª 系統å¯ä»¶ä¿¡ç®± (ä¸¦æ¹æ¼è¨å®æªä¸) |
| | | String senderEmail = applicationProperties.getEmail().getSenderEmail(); |
| | | String consultantEmail = consultantRepository.findOneByAgentNo(appointment.getAgentNo()).get().getEmail(); |
| | | String customerMobile = appointment.getPhone(); |
| | | String normalContent; |
| | | |
| | | if(StringUtils.hasText(customerMobile)) { |
| | | normalContent = "親æçé¡§åæ¨å¥½ï¼æ¨æä¸çä¾èªä¿èª åªåå¹³å°çæ°é ç´å®ï¼è©²å®¢æ¶ææ©è碼çº" + customerMobile + "\n"; |
| | | } |
| | | else { |
| | | normalContent = "親æçé¡§åæ¨å¥½ï¼æ¨æä¸çä¾èªä¿èª åªåå¹³å°çæ°é ç´å®\n"; |
| | | } |
| | | |
| | | Context context = new Context(); |
| | | context.setVariable("content", normalContent); |
| | | context.setVariable("urlHint", getAppointmentDetailUrl(appointment.getId())); |
| | | String content = springTemplateEngine.process("mail/appointmentNotifyEmail", context); |
| | | |
| | | try { |
| | | sendMsgByEmail(senderEmail, consultantEmail, EMAIL_SUBJECT, content, true); |
| | | } catch (SendEmailFailException e) { |
| | | log.debug("send email failed, appointment Id = {}", appointment.getId(), e); |
| | | } |
| | | } |
| | | |
| | | private String getAppointmentNotifyWording(Appointment appointment) { |
| | | String normalContent; |
| | | if(StringUtils.hasText(appointment.getPhone())) { |
| | | normalContent = "親æçé¡§åæ¨å¥½ï¼æ¨æä¸çä¾èªä¿èª åªåå¹³å°çæ°é ç´å®ï¼è©²å®¢æ¶ææ©è碼çº" + appointment.getPhone() + "\n"; |
| | | } |
| | | else { |
| | | normalContent = "親æçé¡§åæ¨å¥½ï¼æ¨æä¸çä¾èªä¿èª åªåå¹³å°çæ°é ç´å®\n"; |
| | | } |
| | | |
| | | String urlContent = "é»æç¶²åï¼" + getAppointmentDetailUrl(appointment.getId()) + " éååªå平尿¥çã"; |
| | | return normalContent + urlContent; |
| | | } |
| | | |
| | | public SendSMSResponse sendMsgBySMS(String toMobile, String content) throws SendSMSFailException { |
| | | SMS smsProperties = applicationProperties.getSms(); |
| | | |
| | | SendSMSRequest sendSMSRequest = new SendSMSRequest(); |
| | |
| | | log.debug("response status code = {}", responseEntity.getStatusCode()); |
| | | log.debug("smsResponse = {}", responseEntity.getBody()); |
| | | |
| | | return responseEntity.getBody(); |
| | | // todo å¯è½éè¦åè£ä¸å¾çºé¯èª¤èçï¼ä½è¦å
測é |
| | | } |
| | | catch (Exception e) { |
| | |
| | | } |
| | | } |
| | | |
| | | public void sendMsgByEmail(String from, String to, String subject, String content, boolean htmlFormat) throws SendEmailFailException{ |
| | | sendMsgByEmail(from, to, subject, content, htmlFormat, Collections.emptyList(), Collections.emptyList()); |
| | | public String sendMsgByHtmlTestTemplateEmail(String from, String to) { |
| | | Context context = new Context(); |
| | | context.setVariable("content", "親æçé¡§åæ¨å¥½ï¼æ¨æä¸çä¾èªä¿èª åªåå¹³å°çæ°é ç´å®\n"); |
| | | context.setVariable("urlHint", getAppointmentDetailUrl(0L)); |
| | | |
| | | String content = springTemplateEngine.process("mail/appointmentNotifyEmail", context); |
| | | return sendMsgByEmail(from, to, EMAIL_SUBJECT, content, true); |
| | | } |
| | | |
| | | public void sendMsgByEmail( |
| | | public String sendMsgByEmail(String from, String to, String subject, String content, boolean htmlFormat) throws SendEmailFailException{ |
| | | return sendMsgByEmail(from, to, subject, content, htmlFormat, Collections.emptyList(), Collections.emptyList()); |
| | | } |
| | | |
| | | public String sendMsgByEmail( |
| | | String fromAddress, String toAddress, String subject, String content, boolean htmlFormat, List<String> toCCAddress, |
| | | List<String> attachments) throws SendEmailFailException |
| | | { |
| | |
| | | sendMailRequest.setHtmlFormat(htmlFormat); |
| | | sendMailRequest.setFunctionId(applicationProperties.getEmail().getFunctionId()); |
| | | |
| | | sendMsgByEmail(sendMailRequest); |
| | | return sendMsgByEmail(sendMailRequest); |
| | | } |
| | | |
| | | public void sendMsgByEmail(SendMailRequest sendMailRequest) throws SendEmailFailException{ |
| | | private String sendMsgByEmail(SendMailRequest sendMailRequest) throws SendEmailFailException{ |
| | | try { |
| | | ResponseEntity<SendMailResponse> responseEntity = |
| | | HttpRequestUtil.postWithJson( applicationProperties.getEmail().getUrl(), sendMailRequest, SendMailResponse.class); |
| | | ResponseEntity<String> responseEntity = |
| | | HttpRequestUtil.postWithJson( applicationProperties.getEmail().getUrl(), sendMailRequest, String.class); |
| | | log.debug("responseEntity = {}", responseEntity); |
| | | |
| | | SendMailResponse sendMailResponse = responseEntity.getBody(); |
| | | log.debug("response status code = {}", responseEntity.getStatusCode()); |
| | | log.debug("emailResponse = {}", responseEntity.getBody()); |
| | | SendMailResponse sendMailResponse = new ObjectMapper().readValue(responseEntity.getBody(), SendMailResponse.class); |
| | | log.debug("sendMailResponse = {}", sendMailResponse); |
| | | |
| | | if(sendMailResponse == null || sendMailResponse.getData() == null || "ADDED".equalsIgnoreCase(sendMailResponse.getData().getMessageStatus())) { |
| | | throw new SendEmailFailException(); |
| | | } |
| | | // if(sendMailResponse == null || sendMailResponse.getData() == null || "ADDED".equalsIgnoreCase(sendMailResponse.getData().getMessageStatus())) { |
| | | // throw new SendEmailFailException(); |
| | | // } |
| | | |
| | | return responseEntity.getBody(); |
| | | } |
| | | catch (SendEmailFailException e) { |
| | | throw e; |
| | |
| | | throw new SendEmailFailException(); |
| | | } |
| | | } |
| | | |
| | | private String getAppointmentDetailUrl(Long appointmentId) { |
| | | return applicationProperties.getFrontEndDomain() + "/myAppointmentList/contactedList?appointmentId=" + appointmentId; |
| | | } |
| | | } |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | public class SendMailResponse { |
| | | private Data data; |
| | | |
| | |
| | | this.data = data; |
| | | } |
| | | |
| | | @JsonIgnoreProperties(ignoreUnknown = true) |
| | | public static class Data { |
| | | private String messageStatus; |
| | | |
| | |
| | | package com.pollex.pam.web.rest; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.service.SendMsgService; |
| | | import com.pollex.pam.service.dto.AppointmentUpdateDTO; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.ResponseEntity; |
| | |
| | | @Autowired |
| | | SatisfactionService satisfactionService; |
| | | |
| | | @Autowired |
| | | SendMsgService sendMsgService; |
| | | |
| | | @PutMapping("") |
| | | public ResponseEntity<Void> updateAppointment(@RequestBody AppointmentUpdateDTO appointment) { |
| | | appointmentService.updateAppointment(appointment); |
| | |
| | | |
| | | @PostMapping("/customer/create") |
| | | public void clientCreateAppointment(@RequestBody AppointmentCreateDTO appointmentCreateDTO) { |
| | | appointmentService.customerCreateAppointment(appointmentCreateDTO); |
| | | } |
| | | Appointment appointment = appointmentService.customerCreateAppointment(appointmentCreateDTO); |
| | | sendMsgService.sendAppointmentNotify(appointment); |
| | | } |
| | | |
| | | @PostMapping("/markAsContacted/{appointmentId}") |
| | | public void markAsContacted(@PathVariable Long appointmentId) { |
| | |
| | | package com.pollex.pam.web.rest; |
| | | |
| | | import com.pollex.pam.repository.AppointmentRepository; |
| | | import com.pollex.pam.service.SendMsgService; |
| | | import com.pollex.pam.service.dto.SendSMSResponse; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.web.bind.annotation.*; |
| | |
| | | @Autowired |
| | | SendMsgService sendMsgService; |
| | | |
| | | @Autowired |
| | | AppointmentRepository appointmentRepository; |
| | | |
| | | @GetMapping("/bySMS") |
| | | public ResponseEntity<Void> bySMS(@RequestParam String toMobile, @RequestParam String content) { |
| | | sendMsgService.sendMsgBySMS(toMobile, content); |
| | | return ResponseEntity.noContent().build(); |
| | | public ResponseEntity<SendSMSResponse> bySMS(@RequestParam String toMobile, @RequestParam String content) { |
| | | return ResponseEntity.ok(sendMsgService.sendMsgBySMS(toMobile, content)); |
| | | } |
| | | |
| | | @GetMapping("/byEmail") |
| | | public ResponseEntity<Void> byEmail( |
| | | public ResponseEntity<String> byEmail( |
| | | @RequestParam String from, |
| | | @RequestParam String to, |
| | | @RequestParam String subject, |
| | | @RequestParam String content, |
| | | @RequestParam boolean htmlFormat |
| | | ) { |
| | | sendMsgService.sendMsgByEmail(from, to, subject, content, htmlFormat); |
| | | return ResponseEntity.noContent().build(); |
| | | return ResponseEntity.ok(sendMsgService.sendMsgByEmail(from, to, subject, content, htmlFormat)); |
| | | } |
| | | |
| | | @GetMapping("/byHtmlEmail") |
| | | public ResponseEntity<String> byHtmlEmail( |
| | | @RequestParam String from, |
| | | @RequestParam String to |
| | | ) { |
| | | return ResponseEntity.ok(sendMsgService.sendMsgByHtmlTestTemplateEmail(from, to)); |
| | | } |
| | | |
| | | @GetMapping("/appointment/{appointmentId}") |
| | | public void sendAppointmentNotify(@PathVariable Long appointmentId) { |
| | | sendMsgService.sendAppointmentNotify(appointmentRepository.findById(appointmentId).get()); |
| | | } |
| | | } |
| | |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.web.bind.annotation.ResponseStatus; |
| | | |
| | | @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "send email failed") |
| | | @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "send sms failed") |
| | | public class SendSMSFailException extends RuntimeException{ |
| | | public SendSMSFailException(){} |
| | | public SendSMSFailException(String message) { |
| | |
| | | e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: http://localhost:3000 |
| | | send-notify-msg: false |
| | | sms: |
| | | url: https://localhost:8081/testSMS |
| | | source-code: ePos |
| | |
| | | email: |
| | | url: https://localhost:8081/testEmail |
| | | function-id: epos |
| | | sender-email: test@pollex.com.tw |
| | |
| | | e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: http://dev.pollex.com.tw:5566/pam |
| | | send-notify-msg: false |
| | | sms: |
| | | url: https://localhost:8081/testSMS |
| | | source-code: ePos |
| | | sender: POS |
| | | sms-type: '0017' |
| | | email: |
| | | url: https://localhost:8081/testEmail |
| | | function-id: epos |
| | | sender-email: test@pollex.com.tw |
| | |
| | | e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: https://vtwlifeopensyssit.pru.intranet.asia/pam |
| | | send-notify-msg: true |
| | | sms: |
| | | url: https://vtwlifewinbo66.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource |
| | | source-code: ePos |
| | |
| | | email: |
| | | url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail |
| | | function-id: epos |
| | | sender-email: test@pollex.com.tw |
| | |
| | | e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: https://vtwlifeopensysuat.pru.intranet.asia/pam |
| | | send-notify-msg: true |
| | | sms: |
| | | url: https://vtwlifewinbo66.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource |
| | | source-code: ePos |
| | |
| | | email: |
| | | url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail |
| | | function-id: epos |
| | | sender-email: test@pollex.com.tw |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <!DOCTYPE html> |
| | | <html xmlns:th="http://www.thymeleaf.org" lang="zh"> |
| | | <head> |
| | | <title>æ°çé ç´å®éç¥</title> |
| | | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
| | | </head> |
| | | <body> |
| | | <p th:text="${content}">親æçé¡§åæ¨å¥½ï¼æ¨æä¸çä¾èªä¿èª åªåå¹³å°çæ°é ç´å®</p> |
| | | <p> |
| | | é»æç¶²åï¼ |
| | | <a th:href="${urlHint}" th:text="${urlHint}">Url Position</a> |
| | | éååªå平尿¥ç |
| | | </p> |
| | | </body> |
| | | </html> |