From 0e27da107dd3329b4d0c11ce7d0173c3bd1358e9 Mon Sep 17 00:00:00 2001 From: Jack <jack.su@pollex.com.tw> Date: 星期三, 19 一月 2022 19:06:52 +0800 Subject: [PATCH] Merge branch 'Phase3' of ssh://dev.pollex.com.tw:29418/pcalife/PAM into Phase3 --- PAMapp/components/Interview/InterviewAdd.vue | 66 ++-- PAMapp/store/login.store.ts | 28 + PAMapp/pages/consultantLogin/index.vue | 9 PAMapp/store/index.ts | 42 -- PAMapp/components/Appointment/AppointmentRecordList.vue | 103 ------ PAMapp/components/Interview/InterviewRecordCard.vue | 99 ++++++ PAMapp/components/BackActionBar.vue | 26 + PAMapp/components/Interview/InterviewCard.vue | 145 +++++++++ PAMapp/store/appointment.store.ts | 7 PAMapp/components/Appointment/AppointmentInterviewList.vue | 141 -------- PAMapp/components/Client/ClientCard.vue | 4 PAMapp/components/Interview/InterviewMsg.vue | 110 +++--- PAMapp/pages/appointment/_appointmentId/recordList/index.vue | 32 ++ PAMapp/shared/models/agent-info.model.ts | 37 +- PAMapp/store/localStorage.ts | 1 PAMapp/pages/appointment/_appointmentId/interviewList/index.vue | 38 ++ PAMapp/pages/appointment/_appointmentId/index.vue | 3 17 files changed, 512 insertions(+), 379 deletions(-) diff --git a/PAMapp/components/Appointment/AppointmentInterviewList.vue b/PAMapp/components/Appointment/AppointmentInterviewList.vue index e7a3325..dc06f5b 100644 --- a/PAMapp/components/Appointment/AppointmentInterviewList.vue +++ b/PAMapp/components/Appointment/AppointmentInterviewList.vue @@ -1,82 +1,20 @@ <template> <div> - <div class="interview__header"> - <div class="mdTxt">蝝赤蝝����</div> - <div class="pam-link-button--lg" - @click="addInterview">+�憓�</div> - </div> - - <template v-if="!interviewList.length"> - <div class="record-card record-card--empty"> - �蝝赤蝝���� - </div> - </template> - - <template v-if="interviewList.length"> - <div - v-for="(item, index) in futureList" - :key="index + 'feature'" - class="interview--future" - @click="editInterview(item)" - > - - <div class="record-card"> - <div class="record-card-date"> - <div> - <UiDateFormat - class="date bold" - :date="item.interviewDate" - onlyShowSection="DAY" /> - </div> - <div> - <UiDateFormat - class="time mt-5 line-space" - :date="item.interviewDate" - onlyShowSection="TIME" /> - </div> - </div> - <div class="record-card-content"> - <span>{{item.content}}</span> - </div> - </div> + <div class="interview__header"> + <div class="mdTxt">蝝赤蝝����</div> + <div class="pam-link-button--lg" + @click="addInterview">+�憓�</div> </div> + <InterviewCard :interviewList="displayList.slice(0, 3)"></InterviewCard> - <section - class="interview--past" - v-for="(item, index) in pastList" - :key="index + 'past'" - @click="editInterview(item)" - > - <div class="record-card"> - <div class="record-card-date"> - <div> - <UiDateFormat - class="date bold" - :date="item.interviewDate" - onlyShowSection="DAY" /> - </div> - <div> - <UiDateFormat - class="time mt-5 line-space" - :date="item.interviewDate" - onlyShowSection="TIME" /> - </div> - </div> - <div class="record-card-content"> - <span>{{item.content}}</span> - </div> - </div> + <section class="text--right mt-30" v-if="interviewList.length > 3"> + <div class="pam-link-button--lg" @click="readMoreBtn">撅��憭�</div> </section> - - <section class="more-log-action"> - <div class="pam-link-button--lg">撅��憭�</div> - </section> - </template> </div> </template> <script lang="ts"> -import { Vue, Component, Prop, Watch, Mutation } from 'nuxt-property-decorator'; +import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'; import { InterviewRecord } from '~/shared/models/appointment.model'; @Component @@ -84,13 +22,8 @@ @Prop() interviewList!: InterviewRecord[]; - @Mutation - updateInterviewRecord!: (data: InterviewRecord) => void; - appointmentId!: string; - - futureList: InterviewRecord[] = []; - pastList: InterviewRecord[] = []; + displayList: InterviewRecord[] = []; ////////////////////////////////////////////////////////////////////// @@ -103,10 +36,9 @@ @Watch('interviewList', {immediate: true}) updateInterviewList() { if (this.interviewList && this.interviewList.length > 0) { - this.futureList = this.interviewList - .filter(item => new Date(item.interviewDate).getTime() >= new Date().getTime()) - this.pastList = this.interviewList - .filter(item => new Date(item.interviewDate).getTime() < new Date().getTime()); + this.displayList = this.interviewList + .map((i) => ({ ...i, sortDate: new Date(i.interviewDate)})) + .sort((preItem, nextItem) => +nextItem.sortDate - +preItem.sortDate); } } @@ -116,9 +48,8 @@ this.$router.push(`/appointment/${this.appointmentId}/interview/new`); } - editInterview(interviewRecord) { - this.updateInterviewRecord(interviewRecord); - this.$router.push(`/appointment/${this.appointmentId}/interview/${interviewRecord.id}`); + readMoreBtn() { + this.$router.push(`/appointment/${this.appointmentId}/interviewList`); } } @@ -129,49 +60,5 @@ display : flex; justify-content: space-between; margin-bottom : 10px; -} -.interview--future{ - border-bottom: 1px solid #CCCCCC; - padding-bottom: 17px; - margin-bottom: 17px; - .record{ - display: flex; - justify-content: space-between; - margin-bottom: 10px; - } -} -.record-card { - height: 62px; - border: 1px solid #707070; - border-radius: 5px; - display: flex; - border-bottom: 1px solid #000; - .record-card-date{ - display: flex; - flex-direction: column; - margin-left: 10px; - margin-right: 10px; - margin-top: 10px; - } - .record-card-content{ - height: 42px; - margin-top: 10px; - margin-right: 10px; - line-height: 1.2; - } - &.record-card--empty { - align-items : center; - background-color: #fff; - color : $MID_GREY; - justify-content : center; - } -} -.line-space{ - letter-spacing: 1px; -} -.more-log-action{ - margin-top: 30px; - display: flex; - justify-content:flex-end; } </style> diff --git a/PAMapp/components/Appointment/AppointmentRecordList.vue b/PAMapp/components/Appointment/AppointmentRecordList.vue index 15e880d..39b2835 100644 --- a/PAMapp/components/Appointment/AppointmentRecordList.vue +++ b/PAMapp/components/Appointment/AppointmentRecordList.vue @@ -2,42 +2,11 @@ <div class="record-log-component"> <div class="mdTxt mt-30 mb-10">蝟餌絞�蝝����</div> - <div v-for="(item, index) in displayLogs" - :key="index"> - <section - class="record-log-card" - > - <div class="record-log-card-date-container"> - <div class="record-log-card-date-container-circle"> - <div class="xxsTxt bold line-height">{{item.createdDate | formatYear}}</div> - <div> - <UiDateFormat - class="xxsTxt bold line-height" - :date="item.createdDate" - onlyShowSection="DAY" /> - </div> - <div> - <UiDateFormat - class="xxsTxt mt-4 line-space" - :date="item.createdDate" - onlyShowSection="TIME" /> - </div> - </div> - </div> - <div class="record-log-msg"> - <div>���赤� - <span v-if="item.email && item.phone">(���陛閮�mail)</span> - <span v-else-if="item.email">(Email)</span> - <span v-else>(���陛閮�)</span> - </div> - <div class="mt-10">���{item.interviewDate | formatDate}}</div> - </div> - </section> - <div class="time-line"></div> - </div> + <InterviewRecordCard :noticeLogsList="displayLogs.slice(0, 3)"></InterviewRecordCard> - <section class="more-log-action"> + <section class="text--right mt-30" v-if="displayLogs.length > 3"> <div class="pam-link-button--lg" + @click="readMoreBtn" >撅��憭�</div> </section> </div> @@ -47,15 +16,7 @@ import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'; import { NoticeLogs } from '~/shared/models/appointment.model'; -@Component({ - filters: { - formatYear(value) { - if (value) { - return new Date(value).getFullYear(); - } - } - } -}) +@Component export default class AppointmentRecordList extends Vue { @Prop() @@ -63,6 +24,8 @@ appointmentId: string = ''; displayLogs : NoticeLogs[] = []; + + ////////////////////////////////////////////////////////////////////// mounted() { this.appointmentId = this.$route.params.appointmentId; @@ -79,53 +42,11 @@ } } + ////////////////////////////////////////////////////////////////////// + + readMoreBtn() { + this.$router.push(`/appointment/${this.appointmentId}/recordList`); + } + } </script> - -<style lang="scss" scoped> -.record-log-component{ - display: flex; - flex-direction: column; - .record-log-card{ - display: flex; - .record-log-card-date-container{ - position:relative; - .record-log-card-date-container-circle{ - display: flex; - flex-direction: column; - width: 56px; - height: 56px; - border-radius: 50%; - border:1px solid $PRIMARY_BLACK; - justify-content: center; - align-items: center; - align-content: center; - } - } - } -} -.mt-4{ - margin-top: 4px; -} -.line-space{ - letter-spacing: 1px; -} -.line-height{ - line-height:1.2; -} -.time-line{ - border-left: 1px solid black; - height: 30px; - margin-left: 28px; - -} -.record-log-msg{ - margin-left: 13px; - margin-top: 10px; -} -.more-log-action{ - display: flex; - justify-content:flex-end; -} - -</style> diff --git a/PAMapp/components/BackActionBar.vue b/PAMapp/components/BackActionBar.vue index 0f628c3..f7981a0 100644 --- a/PAMapp/components/BackActionBar.vue +++ b/PAMapp/components/BackActionBar.vue @@ -14,7 +14,8 @@ import * as _ from 'lodash'; import { Role } from '~/shared/models/enum/Role'; -const roleStorage = namespace('localStorage'); +const appointmentStore = namespace('appointment.store'); +const roleStorage = namespace('localStorage'); @Component export default class UiCarousel extends Vue { @@ -22,11 +23,16 @@ @roleStorage.Getter currentRole!:string; + @appointmentStore.Getter + isCloseAppointment!: boolean; + ////////////////////////////////////////////////////////////////////// goBack(): void { const pathName = this.$route.name; - pathName?.includes('myConsultantList') ? this.$router.push('/') : this.$router.go(-1); + pathName?.includes('myConsultantList') + ? this.$router.push('/') + : this.$router.go(-1); } get label(): string { @@ -78,11 +84,21 @@ featureLabel = 'F&Q 撣貉����'; break; case 'appointment': - const appointmentFeatureLabel = this.$route.name.includes('close') ? '蝯��' : '������'; + const appointmentFeatureLabel = this.$route.name.includes('close') + ? '蝯��' + : this.isCloseAppointment ? '蝯��敦' : '������'; const inInterview = this.$route.name.includes('interview'); const addNewInterview = this.$route.name.includes('new'); - if (inInterview) { - featureLabel = addNewInterview ? '�憓�赤蝝����' : '蝺刻摩蝝赤蝝����'; + const interviewList = this.$route.name.includes('interviewList'); + const recordList = this.$route.name.includes('recordList'); + if (interviewList) { + featureLabel = '蝝赤蝝����'; + } else if (recordList) { + featureLabel = '蝟餌絞�蝝����'; + } else if (inInterview) { + featureLabel = addNewInterview + ? '�憓�赤蝝����' + : '蝺刻摩蝝赤蝝����'; } else { featureLabel = appointmentFeatureLabel; } diff --git a/PAMapp/components/Client/ClientCard.vue b/PAMapp/components/Client/ClientCard.vue index 6afbe92..f203dd0 100644 --- a/PAMapp/components/Client/ClientCard.vue +++ b/PAMapp/components/Client/ClientCard.vue @@ -210,7 +210,7 @@ updateMyAppointmentList!: (data: Appointment) => void; @appointmentStore.Action - setAppointmentDetail!: (appointmentId: number) => Promise<Appointment>; + getAppointmentDetail!: (appointmentId: number) => Promise<Appointment>; @appointmentStore.Getter appointmentProgress!: ContactStatus; @@ -256,7 +256,7 @@ ////////////////////////////////////////////////////////////////////// viewAppointmentDetail(): void { - this.setAppointmentDetail(this.client.id).then((_) => { + this.getAppointmentDetail(this.client.id).then((_) => { this.$router.push(`/appointment/${this.client.id}`); }); } diff --git a/PAMapp/components/Interview/InterviewAdd.vue b/PAMapp/components/Interview/InterviewAdd.vue index 3f867d8..7a47229 100644 --- a/PAMapp/components/Interview/InterviewAdd.vue +++ b/PAMapp/components/Interview/InterviewAdd.vue @@ -1,6 +1,6 @@ <template> <div class="edit-appointment-record"> - <div class="edit-appointment-record-date" v-if="interviewId"> + <div class="edit-appointment-record-date" v-if="interviewId && interviewRecord"> <span>{{interviewRecord.createdDate | formatDate}} 撱箇��</span> <span>{{interviewRecord.lastModifiedDate | formatDate}} ��</span> </div> @@ -85,25 +85,26 @@ <InterviewMsg :isVisible.sync="showInterviewMsgPopup" + :client="appointmentDetail" @closeDialog="closePopup" ></InterviewMsg> </div> </template> <script lang="ts"> -import { InterviewRecord, InterviewRecordInfo } from '~/shared/models/appointment.model'; -import { Vue, Component, Prop, State, Mutation, Watch, Action } from 'nuxt-property-decorator'; +import { Appointment, InterviewRecord, InterviewRecordInfo } from '~/shared/models/appointment.model'; +import { Vue, Component, Watch, namespace } from 'nuxt-property-decorator'; import appointmentService from '~/shared/services/appointment.service'; + +const appointmentStore = namespace('appointment.store'); @Component export default class InterviewAdd extends Vue { - @State - interviewRecord!: InterviewRecord; - @Mutation - updateInterviewRecord!: (data: InterviewRecord) => void; + @appointmentStore.State + appointmentDetail!: Appointment; - @Mutation - clearInterviewRecord!: () => void; + @appointmentStore.Action + updateAppointmentDetail!: (id: number) => Appointment; interviewTime = ''; content = ''; @@ -121,38 +122,29 @@ defaultValue = ''; + interviewRecord!: InterviewRecord; + //////////////////////////////////////////////////////////////////// mounted() { this.interviewId = this.$route.params.interviewId; this.appointmentId = this.$route.params.appointmentId; - const isEditPage = this.interviewId && this.interviewRecord; - if (isEditPage) { - this.checkInterviewRecord(); - } - } - private checkInterviewRecord() { - if (this.interviewRecord.appointmentId !== +this.appointmentId - || this.interviewRecord.id !== +this.interviewId) { - appointmentService.getAppointmentDetail(+this.appointmentId).then((data) => { - const currentInterviewRecord = data.interviewRecordDTOs.filter(item => item.id === +this.interviewId)[0]; - this.updateInterviewRecord(currentInterviewRecord); - }) - } - } - - destroyed() { - this.clearInterviewRecord(); + this.onAppointmentDetailChange(); } //////////////////////////////////////////////////////////////////// - @Watch('interviewRecord', {immediate: true}) - watchInterviewRecord() { - if (this.interviewRecord && this.interviewRecord.content) { - this.content = this.interviewRecord.content; - this.defaultValue = this.interviewRecord.interviewDate; + @Watch('appointmentDetail', {immediate: true}) + onAppointmentDetailChange() { + if (this.appointmentDetail && this.appointmentDetail.id === +this.appointmentId) { + this.interviewRecord = this.appointmentDetail.interviewRecordDTOs + .filter(item => item.id === +this.interviewId)[0]; + + if (this.interviewRecord && this.interviewId) { + this.content = this.interviewRecord.content; + this.defaultValue = this.interviewRecord.interviewDate; + } } } @@ -173,6 +165,7 @@ } this.updateRecord(updateInterviewRecord); } + this.updateAppointmentDetail(+this.appointmentId); } private createdRecord(interviewRecordInfo) { @@ -197,14 +190,11 @@ } } - closePopup() { - this.$router.push(`/appointment/${this.appointmentId}`); - } - deleteInterviewRecord() { appointmentService.deleteInterviewRecord(this.interviewId).then(res => { this.confirmTxt = '������'; this.showConfirmPopup = true; + this.updateAppointmentDetail(+this.appointmentId); }); } @@ -214,10 +204,14 @@ this.defaultValue = this.interviewRecord.interviewDate; this.isEdit = false; } else { - this.$router.push(`/appointment/${this.appointmentId}`); + this.$router.go(-1); } } + closePopup() { + this.$router.go(-1); + } + //////////////////////////////////////////////////////////////////// get formatInterviewDate() { diff --git a/PAMapp/components/Interview/InterviewCard.vue b/PAMapp/components/Interview/InterviewCard.vue new file mode 100644 index 0000000..e3f4f54 --- /dev/null +++ b/PAMapp/components/Interview/InterviewCard.vue @@ -0,0 +1,145 @@ +<template> + <div> + <template v-if="!interviewList.length"> + <div class="record-card record-card--empty"> + �蝝赤蝝���� + </div> + </template> + + <template v-else> + <div class="interview--future"> + <div class="record-card mb-10" + v-for="(item, index) in futureList" + :key="index + 'feature'" + @click="editInterview(item)" + > + <div class="record-card-date"> + <div> + <UiDateFormat + class="date bold" + :date="item.interviewDate" + onlyShowSection="DAY" /> + </div> + <div> + <UiDateFormat + class="time mt-5 line-space" + :date="item.interviewDate" + onlyShowSection="TIME" /> + </div> + </div> + <div class="record-card-content"> + <span>{{item.content}}</span> + </div> + </div> + </div> + + <section class="interview--past" v-if="pastList.length"> + <div class="record-card mb-10" + v-for="(item, index) in pastList" + :key="index + 'past'" + @click="editInterview(item)" + > + <div class="record-card-date"> + <div> + <UiDateFormat + class="date bold" + :date="item.interviewDate" + onlyShowSection="DAY" /> + </div> + <div> + <UiDateFormat + class="time mt-5 line-space" + :date="item.interviewDate" + onlyShowSection="TIME" /> + </div> + </div> + <div class="record-card-content"> + <span>{{item.content}}</span> + </div> + </div> + </section> + </template> + </div> +</template> + +<script lang="ts"> +import { Component, Prop, Vue, Watch } from "nuxt-property-decorator"; +import { InterviewRecord } from "~/shared/models/appointment.model"; + +@Component +export default class InterviewCard extends Vue { + + @Prop() + interviewList!: InterviewRecord[]; + + futureList: InterviewRecord[] = []; + pastList: InterviewRecord[] = []; + + appointmentId!: number; + + mounted() { + this.appointmentId = +this.$route.params.appointmentId; + } + + @Watch('interviewList', {immediate: true}) + onInterviewListChange() { + if (this.interviewList.length > 0) { + this.futureList = this.interviewList + .filter(item => new Date(item.interviewDate).getTime() >= new Date().getTime()) + .sort((preItem, nextItem) => +new Date(nextItem.interviewDate) - +new Date(preItem.interviewDate)); + this.pastList = this.interviewList + .filter(item => new Date(item.interviewDate).getTime() < new Date().getTime()) + .sort((preItem, nextItem) => +new Date(nextItem.interviewDate) - +new Date(preItem.interviewDate)); + } + + } + + editInterview(interviewRecord) { + this.$router.push(`/appointment/${this.appointmentId}/interview/${interviewRecord.id}`); + } +} +</script> + +<style lang="scss" scoped> +.interview--future{ + .record{ + display: flex; + justify-content: space-between; + margin-bottom: 10px; + } +} +.interview--past { + border-top: 1px solid #CCCCCC; + padding-top: 17px; + margin-top: 17px; +} +.record-card { + height: 62px; + border: 1px solid #707070; + border-radius: 5px; + display: flex; + border-bottom: 1px solid #000; + .record-card-date{ + display: flex; + flex-direction: column; + margin-left: 10px; + margin-right: 10px; + margin-top: 10px; + } + .record-card-content{ + height: 42px; + margin-top: 10px; + margin-right: 10px; + line-height: 1.2; + } + &.record-card--empty { + align-items : center; + background-color: #fff; + color : $MID_GREY; + justify-content : center; + } +} +.line-space{ + letter-spacing: 1px; +} +</style> diff --git a/PAMapp/components/Interview/InterviewMsg.vue b/PAMapp/components/Interview/InterviewMsg.vue index 7d671ce..eaffa87 100644 --- a/PAMapp/components/Interview/InterviewMsg.vue +++ b/PAMapp/components/Interview/InterviewMsg.vue @@ -1,5 +1,5 @@ <template> - <div> + <div class="interview-msg-component"> <el-dialog :visible.sync="dialogVisible" :width="dialogWidth" @@ -14,10 +14,10 @@ <el-input type="textarea" - :rows="9" + :autosize="true" placeholder="蝝赤�" resize="none" - v-model="interviewTxt"> + v-model="isInterviewTxt"> </el-input> <div class="mdTxt mt-30 mb-10">����赤��挾</div> @@ -41,29 +41,23 @@ </div> </template> <script lang="ts"> -import { Vue, Component, Prop, PropSync, Emit, namespace } from 'nuxt-property-decorator'; +import { Vue, Component, Prop, PropSync, Emit, Action, namespace } from 'nuxt-property-decorator'; import appointmentService from '~/shared/services/appointment.service'; import { Appointment, ToInformAppointment } from '~/shared/models/appointment.model'; -import { ContactStatus } from '~/shared/models/enum/contact-status'; +import { AgentInfo } from '~/shared/models/agent-info.model'; +const loginStore = namespace('login.store'); const appointmentStore = namespace('appointment.store'); @Component export default class InterviewMsg extends Vue { - @appointmentStore.Action - getMyAppointmentList!: () => Promise<Appointment[]>; - + @Action + storeMyAppointmentList!: () => Promise<number>; @appointmentStore.Action - updateMyAppointmentList!:(appointment: Appointment) => Appointment[]; - - @appointmentStore.Action - updateAppointmentDetail!: (appointmentId: number) => Promise<Appointment>; - - @appointmentStore.State - appointmentDetail!: Appointment; + updateAppointmentDetail!: (id: number) => Appointment; @PropSync('isVisible') dialogVisible!: boolean; @@ -79,12 +73,13 @@ return; } - interviewTime = ''; - interviewTxt = ''; + @loginStore.State + loginConsultant!: AgentInfo; + isShowSuccessAlert = false; - contactStatus = ContactStatus; - + interviewTxt = ""; + interviewTime = ''; ////////////////////////////////////////////////////////////////////// addInterview() { @@ -95,57 +90,58 @@ message : this.interviewTxt, phone : this.client?.phone, }; - appointmentService.informAppointment(appointmentInformation).then((_) => { this.isShowSuccessAlert = true ; - const updatedAppointment = { - ...this.appointmentDetail, - communicateStatus: this.contactStatus.CONTACTED, - }; - this.updateMyAppointmentList(updatedAppointment); - this.updateAppointmentDetail(updatedAppointment.id); + this.updateAppointmentDetail(this.client.id); }); } closeAllDialog() { this.isShowSuccessAlert = false ; this.dialogVisible = false; - this.getMyAppointmentList(); + this.storeMyAppointmentList(); + } + + get isInterviewTxt() : string{ + return this.interviewTxt = "�憟踝��靽���像����憿批��" + this.loginConsultant?.name + "嚗�����������銝膩������蝜�"+"\n"+"隞乩����閰梯�Ⅳ/Email嚗�"+"\n" + this.loginConsultant?.phoneNumber + "\n" + this.loginConsultant?.email + "\n"+"�甇斗���靘選����蝜恬�����" } } </script> -<style lang="scss" scoped> -.msg-dialog-title{ - display: flex; - justify-content: center; - margin-bottom:30px; - color: $PRIMARY_BLACK; -} -.send-msg-nav{ - display: flex; - justify-content: space-between; - margin-bottom: 10px; - color: $PRIMARY_BLACK; -} -.el-dialog{ - width:90% -} -.el-textarea__inner{ - font-size: 20px; - padding:10px; - text-align: justify; - font-weight: 500; -} -.msg-dialog-btn{ - margin-top: 30px; - display: flex; - justify-content: center; -} -.invite-review{ +<style lang="scss" > +.interview-msg-component{ + + .msg-dialog-title{ display: flex; - flex-direction: column; - align-items: center; + justify-content: center; + margin-bottom:30px; + color: $PRIMARY_BLACK; } + .send-msg-nav{ + display: flex; + justify-content: space-between; + margin-bottom: 10px; + color: $PRIMARY_BLACK; + } + .el-dialog{ + width:90% + } + .el-textarea__inner{ + font-size: 20px; + padding:10px; + text-align: justify; + font-weight: 600; + } + .msg-dialog-btn{ + margin-top: 30px; + display: flex; + justify-content: center; + } + .invite-review{ + display: flex; + flex-direction: column; + align-items: center; + } +} </style> diff --git a/PAMapp/components/Interview/InterviewRecordCard.vue b/PAMapp/components/Interview/InterviewRecordCard.vue new file mode 100644 index 0000000..34f1d28 --- /dev/null +++ b/PAMapp/components/Interview/InterviewRecordCard.vue @@ -0,0 +1,99 @@ +<template> + <div class="record-log-component"> + <div v-for="(item, index) in noticeLogsList" + :key="index"> + <section + class="record-log-card" + > + <div class="record-log-card-date-container"> + <div class="record-log-card-date-container-circle"> + <div class="xxsTxt bold line-height">{{item.createdDate | formatYear}}</div> + <div> + <UiDateFormat + class="xxsTxt bold line-height" + :date="item.createdDate" + onlyShowSection="DAY" /> + </div> + <div> + <UiDateFormat + class="xxsTxt mt-4 line-space" + :date="item.createdDate" + onlyShowSection="TIME" /> + </div> + </div> + </div> + <div class="record-log-msg"> + <div>���赤� + <span v-if="item.email && item.phone">(���陛閮�mail)</span> + <span v-else-if="item.email">(Email)</span> + <span v-else>(���陛閮�)</span> + </div> + <div class="mt-10">���{item.interviewDate | formatDate}}</div> + </div> + </section> + <div class="time-line"></div> + </div> + </div> +</template> + +<script lang="ts"> +import { Component, Prop, Vue } from "nuxt-property-decorator"; +import { NoticeLogs } from "~/shared/models/appointment.model"; + +@Component({ + filters: { + formatYear(value) { + if (value) { + return new Date(value).getFullYear(); + } + } + } +}) +export default class RecordCard extends Vue { + @Prop() + noticeLogsList!: NoticeLogs[]; +} +</script> + +<style lang="scss" scoped> +.record-log-component{ + display: flex; + flex-direction: column; + .record-log-card{ + display: flex; + .record-log-card-date-container{ + position:relative; + .record-log-card-date-container-circle{ + display: flex; + flex-direction: column; + width: 56px; + height: 56px; + border-radius: 50%; + border:1px solid $PRIMARY_BLACK; + justify-content: center; + align-items: center; + align-content: center; + } + } + } +} +.mt-4{ + margin-top: 4px; +} +.line-space{ + letter-spacing: 1px; +} +.line-height{ + line-height:1.2; +} +.time-line{ + border-left: 1px solid black; + height: 30px; + margin-left: 28px; + +} +.record-log-msg{ + margin-left: 13px; + margin-top: 10px; +} +</style> \ No newline at end of file diff --git a/PAMapp/pages/appointment/_appointmentId/index.vue b/PAMapp/pages/appointment/_appointmentId/index.vue index 2994205..b4a7da0 100644 --- a/PAMapp/pages/appointment/_appointmentId/index.vue +++ b/PAMapp/pages/appointment/_appointmentId/index.vue @@ -91,12 +91,9 @@ </template> <script lang="ts"> -import { Context } from '@nuxt/types'; - import { Vue, Component } from 'vue-property-decorator'; import { namespace } from 'nuxt-property-decorator'; -import appointmentService from '~/shared/services/appointment.service'; import { Appointment } from '~/shared/models/appointment.model'; import { ContactStatus } from '~/shared/models/enum/contact-status'; diff --git a/PAMapp/pages/appointment/_appointmentId/interviewList/index.vue b/PAMapp/pages/appointment/_appointmentId/interviewList/index.vue new file mode 100644 index 0000000..197d949 --- /dev/null +++ b/PAMapp/pages/appointment/_appointmentId/interviewList/index.vue @@ -0,0 +1,38 @@ + +<template> + <div> + <div class="text--right mb-30"> + <div class="pam-link-button--lg" + @click="addInterview">+�憓�</div> + </div> + <InterviewCard :interviewList="appointmentDetail.interviewRecordDTOs"></InterviewCard> + </div> +</template> + +<script lang="ts"> +import { Component, namespace, Vue } from "nuxt-property-decorator"; +import { Appointment } from "~/shared/models/appointment.model"; + +const appointmentStore = namespace('appointment.store'); + +@Component +export default class InterviewList extends Vue { + @appointmentStore.State + appointmentDetail!: Appointment; + + appointmentId!: number; + + //////////////////////////////////////////////////////// + + mounted() { + this.appointmentId = +this.$route.params.appointmentId; + } + + //////////////////////////////////////////////////////// + + addInterview(): void { + this.$router.push(`/appointment/${this.appointmentId}/interview/new`); + } + +} +</script> \ No newline at end of file diff --git a/PAMapp/pages/appointment/_appointmentId/recordList/index.vue b/PAMapp/pages/appointment/_appointmentId/recordList/index.vue new file mode 100644 index 0000000..89d7bf7 --- /dev/null +++ b/PAMapp/pages/appointment/_appointmentId/recordList/index.vue @@ -0,0 +1,32 @@ + + +<template> + <InterviewRecordCard :noticeLogsList="displayLogs"></InterviewRecordCard> +</template> + +<script lang="ts"> +import { Component, namespace, Vue, Watch } from "nuxt-property-decorator"; +import { Appointment, NoticeLogs } from "~/shared/models/appointment.model"; + +const appointmentStore = namespace('appointment.store'); + +@Component +export default class RecordList extends Vue { + @appointmentStore.State + appointmentDetail!: Appointment; + + displayLogs: NoticeLogs[] = []; + + //////////////////////////////////////////////////////// + + @Watch('appointmentDetail', {immediate: true}) + onAppointmentDetailChange() { + if (this.appointmentDetail?.appointmentNoticeLogs.length) { + this.displayLogs = this.appointmentDetail?.appointmentNoticeLogs + .map((i) => ({ ...i, sortDate: new Date(i.createdDate)})) + .sort((preItem, nextItem) => +nextItem.sortDate - +preItem.sortDate); + } + } + +} +</script> \ No newline at end of file diff --git a/PAMapp/pages/consultantLogin/index.vue b/PAMapp/pages/consultantLogin/index.vue index 2358123..dcebb79 100644 --- a/PAMapp/pages/consultantLogin/index.vue +++ b/PAMapp/pages/consultantLogin/index.vue @@ -61,8 +61,11 @@ import { Role } from '~/shared/models/enum/Role'; import messageBoxService from '~/shared/services/message-box.service'; import loginService from '~/shared/services/login.service' +import { AgentInfo } from '~/shared/models/agent-info.model'; + const loginStore = namespace('login.store'); const roleStorage = namespace('localStorage'); + @Component({ layout: 'home' }) @@ -75,6 +78,9 @@ @roleStorage.Mutation storageConsultantId!:(id:string) => void; + + @loginStore.Action + getLoginConsultantDetail!: (agentNo: string) => Promise<AgentInfo>; consultantDto = { password: '', @@ -130,7 +136,7 @@ private verify():void{ loginService.getVerificationStatus(this.verificationCode).then( verifySuccess => { if(verifySuccess.data){ - this.loginWithConsultant() + this.loginWithConsultant(); }else{ this.clearValue(); this.regenerateImgOfVerification(); @@ -141,6 +147,7 @@ private loginWithConsultant(): void { loginService.logInToConsultant(this.consultantDto).then(res => { + this.getLoginConsultantDetail(this.consultantDto.username); this.storageIdToken(res.data.id_token); this.storageRole(Role.ADMIN); this.storageConsultantId(this.consultantDto.username) diff --git a/PAMapp/shared/models/agent-info.model.ts b/PAMapp/shared/models/agent-info.model.ts index b91054d..e6af754 100644 --- a/PAMapp/shared/models/agent-info.model.ts +++ b/PAMapp/shared/models/agent-info.model.ts @@ -1,21 +1,22 @@ export interface AgentInfo { - name : string; - agentNo : string; - role : string; - img : string; - avgScore : number; - title : string; - phoneNumber : string; - serveArea : string; - companyAddress : string; - latestLoginTime : Date ; - seniority : string; - suitability : number; - evaluation : number; - expertise : string[]; - concept : string; - experiences : string; - awards : string; - gender : string, + agentNo : string; + avgScore : number; + awards : string; communicationStyle: string; + companyAddress : string; + concept : string; + email? : string; + evaluation : number; + experiences : string; + expertise : string[]; + gender : string, + img : string; + latestLoginTime : Date ; + name : string; + phoneNumber : string; + role : string; + seniority : string; + serveArea : string; + suitability : number; + title : string; } diff --git a/PAMapp/store/appointment.store.ts b/PAMapp/store/appointment.store.ts index 8c7b3e5..e8c8eff 100644 --- a/PAMapp/store/appointment.store.ts +++ b/PAMapp/store/appointment.store.ts @@ -35,6 +35,11 @@ .filter(item => item.communicateStatus === this.contactStatus.DONE || item.communicateStatus === this.contactStatus.CLOSE ).length; } + get isCloseAppointment(): boolean { + const closedStatusList = [this.contactStatus.DONE, this.contactStatus.CLOSE, this.contactStatus.CANCEL]; + return closedStatusList.includes(this.appointmentDetail!.communicateStatus); + } + ////////////////////////////////////////////////////////////////////// @Mutation @@ -63,7 +68,7 @@ } @Action({ commit: 'SET_APPOINTMENT'}) - async setAppointmentDetail(appointmentId: number): Promise<Appointment> { + async getAppointmentDetail(appointmentId: number): Promise<Appointment> { if (this.appointmentDetail && this.appointmentDetail.id === appointmentId) { return this.appointmentDetail; } else { diff --git a/PAMapp/store/index.ts b/PAMapp/store/index.ts index 5ccdda4..f322519 100644 --- a/PAMapp/store/index.ts +++ b/PAMapp/store/index.ts @@ -1,18 +1,13 @@ -import { StrictQueryParams } from '~/shared/models/strict-query.model'; import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators' - -import { getFavoriteFromStorage, setFavoriteToStorage } from '~/shared/storageConsultant'; 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 '~/shared/models/consultant.model'; -import { Appointment, AppointmentLog, InterviewRecord } from '~/shared/models/appointment.model'; -import { AgentOfStrictQuery } from '~/shared/models/strict-query.model'; -import { AgentInfo } from '~/shared/models/agent-info.model'; -import { agentCommunicationStyleList } from '~/shared/const/agent-communication-style-list'; +import { getFavoriteFromStorage, setFavoriteToStorage } from '~/shared/storageConsultant'; +import { AppointmentLog } from '~/shared/models/appointment.model'; +import { AgentOfStrictQuery, StrictQueryParams } from '~/shared/models/strict-query.model'; + @Module export default class Store extends VuexModule { recommendList: Consultant[] = []; @@ -20,16 +15,6 @@ myConsultantList: Consultant[] = []; myAppointmentReviewLogList: AppointmentLog[] = []; - interviewRecord: InterviewRecord = { - appointmentId : 0, - content : '', - createdBy : '', - createdDate : '', - id : 0, - interviewDate : '', - lastModifiedBy : '', - lastModifiedDate: '' - } get isUserLogin() { return this.context.getters['localStorage/isUserLogin']; @@ -53,25 +38,6 @@ @Mutation updateMyAppointmentReviewLog(data: AppointmentLog[]) { this.myAppointmentReviewLogList = data; - } - - @Mutation - updateInterviewRecord(data: InterviewRecord) { - this.interviewRecord = data; - } - - @Mutation - clearInterviewRecord() { - this.interviewRecord = { - appointmentId : 0, - content : '', - createdBy : '', - createdDate : '', - id : 0, - interviewDate : '', - lastModifiedBy : '', - lastModifiedDate: '' - } } @Action diff --git a/PAMapp/store/localStorage.ts b/PAMapp/store/localStorage.ts index 17c0a95..4c6a959 100644 --- a/PAMapp/store/localStorage.ts +++ b/PAMapp/store/localStorage.ts @@ -75,6 +75,7 @@ localStorage.removeItem('current_role'); localStorage.removeItem('consultant_id'); localStorage.removeItem('appointment'); + localStorage.removeItem('login_consultant'); this.id_token = localStorage.getItem('id_token'); this.current_role = localStorage.getItem('current_role'); this.consultant_id = localStorage.getItem('consultant_id'); diff --git a/PAMapp/store/login.store.ts b/PAMapp/store/login.store.ts new file mode 100644 index 0000000..a26fc2f --- /dev/null +++ b/PAMapp/store/login.store.ts @@ -0,0 +1,28 @@ +import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators' + +import myConsultantService from '~/shared/services/my-consultant.service'; +import { AgentInfo } from '~/shared/models/agent-info.model'; + +@Module +export default class AppointmentStore extends VuexModule { + + loginConsultant?: AgentInfo = JSON.parse(localStorage.getItem('login_consultant')!); + + ////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////// + + @Mutation + SET_LOGIN_CONSULTANT(agentInfo: AgentInfo): void { + this.loginConsultant = agentInfo; + localStorage.setItem('login_consultant', JSON.stringify(agentInfo)); + } + + ////////////////////////////////////////////////////////////////////// + + @Action({ commit: 'SET_LOGIN_CONSULTANT' }) + async getLoginConsultantDetail(agentNo: string): Promise<AgentInfo> { + return await myConsultantService.getConsultantDetail(agentNo).then((res) => res); + } + +} -- Gitblit v1.8.0