保誠-保戶業務員媒合平台
Mila
2022-01-18 b1b1fa9058a8e7df07c25cf6d5be1678a042ab7e
update: TODO#134382 [顧問管理流程] 刪除/編輯約訪紀錄
修改10個檔案
386 ■■■■ 已變更過的檔案
PAMapp/assets/scss/vendors/elementUI/_dateTimePicker.scss 3 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Appointment/AppointmentInterviewList.vue 85 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/DateTimePicker.vue 15 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Interview/InterviewAdd.vue 216 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Ui/UiDatePicker.vue 13 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Ui/UiTimePicker.vue 15 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/appointment/_appointmentId/index.vue 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/shared/models/appointment.model.ts 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/shared/services/appointment.service.ts 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/store/index.ts 33 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/scss/vendors/elementUI/_dateTimePicker.scss
@@ -52,6 +52,9 @@
                    td.available:hover {
                        color: $CORAL;
                    }
                    td.today.current span {
                        color: $PRIMARY_WHITE;
                    }
                }
            }
            .el-year-table {
PAMapp/components/Appointment/AppointmentInterviewList.vue
@@ -13,27 +13,57 @@
      </template>
      <template v-if="interviewList.length">
        <div class="interview--future">
        <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">
                    <span class="bold">01/10</span>
                    <span class="mt-5 line-space">09:00</span>
                    <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>預計提醒客戶約在公司樓下咖啡廳,推薦熊安心醫療險與防疫保單</span>
                    <span>{{item.content}}</span>
                </div>
            </div>
        </div>
        <section class="interview--past">
        <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">
                    <span class="bold">01/08</span>
                    <span class="mt-5 line-space">09:00</span>
                    <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>王聰明回信提供電話號碼,去電推薦預約時間</span>
                    <span>{{item.content}}</span>
                </div>
            </div>
        </section>
@@ -46,18 +76,49 @@
</template>
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator';
import { Vue, Component, Prop, Watch, Mutation } from 'nuxt-property-decorator';
import { InterviewRecord } from '~/shared/models/appointment.model';
@Component
export default class AppointmentInterviewList extends Vue {
  @Prop()
  interviewList!: InterviewRecord[];
  interviewList = [];
  @Mutation
  updateInterviewRecord!: (data: InterviewRecord) => void;
  appointmentId!: string;
  futureList: InterviewRecord[] = [];
  pastList: InterviewRecord[] = [];
  //////////////////////////////////////////////////////////////////////
  mounted() {
      this.appointmentId = this.$route.params.appointmentId;
  }
  //////////////////////////////////////////////////////////////////////
  @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());
      }
  }
  //////////////////////////////////////////////////////////////////////
  addInterview(): void {
    const appointmentId = this.$route.params.appointmentId;
    this.$router.push(`/appointment/${appointmentId}/interview/new`);
    this.$router.push(`/appointment/${this.appointmentId}/interview/new`);
  }
  editInterview(interviewRecord) {
    this.updateInterviewRecord(interviewRecord);
    this.$router.push(`/appointment/${this.appointmentId}/interview/${interviewRecord.id}`);
  }
}
PAMapp/components/DateTimePicker.vue
@@ -2,19 +2,28 @@
<template>
    <div class="dateTime">
        <UiDatePicker @changeDate="changeDateTime($event, 'date')"></UiDatePicker>
        <UiTimePicker @changeTime="changeDateTime($event, 'time')"></UiTimePicker>
        <UiDatePicker
            @changeDate="changeDateTime($event, 'date')"
            :defaultValue="defaultValue"
        ></UiDatePicker>
        <UiTimePicker
            @changeTime="changeDateTime($event, 'time')"
            :defaultValue="defaultValue"
        ></UiTimePicker>
    </div>
</template>
<script lang="ts">
import { Component, Emit, Vue } from "nuxt-property-decorator";
import { Component, Emit, Prop, Vue } from "nuxt-property-decorator";
@Component
export default class DateTimePicker extends Vue {
    changeDate!: Date;
    changeTime!: string;
    @Prop()
    defaultValue!: string;
    @Emit('changeDateTime')
    changeDateTime(event, type) {
        if (type === 'date') {
PAMapp/components/Interview/InterviewAdd.vue
@@ -1,16 +1,38 @@
<template>
  <div class="edit-appointment-record">
      <div class="edit-appointment-record-date">
          <span>今天 11:00 建立</span>
          <span>今天 11:00 更新</span>
      <div class="edit-appointment-record-date" v-if="interviewId">
          <span>{{interviewRecord.createdDate | formatDate}} 建立</span>
          <span>{{interviewRecord.lastModifiedDate | formatDate}} 更新</span>
      </div>
      <div class="mdTxt mb-10">約訪時間</div>
      <el-row class="mdTxt mb-10">
          <el-col :xs="16" :sm="20">約訪時間</el-col>
          <el-col :xs="8" :sm="4" class="text--right" v-if="interviewId">
              <span
                v-if="!isEdit"
                class="mr-10 text--primary text--underline cursor--pointer"
                @click="showCancelPopUp = true"
              >刪除</span>
              <span
                v-if="!isEdit"
                class="text--primary text--underline cursor--pointer"
                @click="isEdit = !isEdit"
              >編輯</span>
          </el-col>
      </el-row>
      <template v-if="!interviewId || isEdit">
      <DateTimePicker
        @changeDateTime="interviewTime = $event"
            :defaultValue="defaultValue"
      ></DateTimePicker>
      </template>
      <template v-else>
          <div class="mdTxt lighter mt-20">
              {{formatInterviewDate}}
          </div>
      </template>
      <div class="mdTxt mb-10 mt-10">約訪紀錄</div>
      <div class="mdTxt mb-10 mt-30">約訪紀錄</div>
      <template v-if="!interviewId || isEdit">
      <el-input
        type="textarea"
        :rows="5"
@@ -19,58 +41,188 @@
        v-model="content"
      >
      </el-input>
      <div class="edit-appointment-record-btn">
          <el-button>取消</el-button>
          <el-button :disabled="!interviewTime || !content" @click="createInterviewRecord">確定</el-button>
      </template>
      <template v-else>
          <div class="mdTxt lighter mt-20">
              {{content}}
          </div>
      </template>
      <div class="edit-appointment-record-btn" v-if="!interviewId || isEdit">
          <el-button @click="cancel">取消</el-button>
          <el-button
            :disabled="!interviewTime || !content"
            @click="saveInterviewRecord"
          >確定</el-button>
      </div>
      <PopUpFrame :isOpen.sync="isConfirmPopup"
        @closePopUp="closeConfirmPopup">
        <div class="text--center mdTxt">新增成功</div>
      <PopUpFrame :isOpen.sync="showCancelPopUp"
         @closePopUp="showCancelPopUp = false"
      >
        <div class="text--center mdTxt">是否刪除此筆約訪記錄?</div>
        <div class="text--center mt-30">
            <el-button @click="closeConfirmPopup" type="primary">確定</el-button>
            <el-button @click="showCancelPopUp = false">否</el-button>
            <el-button @click="deleteInterviewRecord" type="primary">是</el-button>
        </div>
    </PopUpFrame>
      <PopUpFrame :isOpen.sync="showConfirmPopup"
        @closePopUp="closePopup">
        <div class="text--center mdTxt">{{confirmTxt}}!</div>
        <div class="text--center mt-30">
            <el-button @click="closePopup" type="primary">確定</el-button>
        </div>
      </PopUpFrame>
      <PopUpFrame :isOpen.sync="showFutureDateConfirmPopup"
        @closePopUp="closePopup">
        <div class="text--center mdTxt">{{confirmTxt}}!</div>
        <div class="text--center mdTxt">立即發送約訪通知?</div>
        <div class="text--center mt-30">
            <el-button @click="closePopup">先不發送</el-button>
            <el-button @click="showInterviewMsgPopup = true" type="primary">傳送約訪通知</el-button>
        </div>
      </PopUpFrame>
      <InterviewMsg
        :isVisible.sync="showInterviewMsgPopup"
        @closeDialog="closePopup"
      ></InterviewMsg>
  </div>
</template>
<script lang="ts">
import { AppointmentLog, InterviewRecordInfo } from '~/shared/models/appointment.model';
import { Vue, Component, Prop } from 'nuxt-property-decorator';
import authService from '~/shared/services/auth.service';
import { InterviewRecord, InterviewRecordInfo } from '~/shared/models/appointment.model';
import { Vue, Component, Prop, State, Mutation, Watch, Action } from 'nuxt-property-decorator';
import appointmentService from '~/shared/services/appointment.service';
@Component
export default class InterviewAdd extends Vue {
    @State
    interviewRecord!: InterviewRecord;
    @Mutation
    updateInterviewRecord!: (data: InterviewRecord) => void;
    @Mutation
    clearInterviewRecord!: () => void;
    interviewTime = '';
    content = '';
    isConfirmPopup = false;
    // @Prop()
    // myAppointmentReviewLogList!: AppointmentLog[];
    interviewId = '';
    appointmentId = '';
    confirmTxt: '新增成功' | '編輯成功' | '刪除成功' = '新增成功';
    // isUserLogin = false;
    isEdit = false;
    //////////////////////////////////////////////////////////////////////
    // mounted() {
    //   this.isUserLogin = authService.isUserLogin();
    // }
    showConfirmPopup = false;
    showCancelPopUp = false;
    showInterviewMsgPopup = false;
    showFutureDateConfirmPopup = false;
    createInterviewRecord() {
    defaultValue!: Date;
    ////////////////////////////////////////////////////////////////////
    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();
    }
    ////////////////////////////////////////////////////////////////////
    @Watch('interviewRecord', {immediate: true})
    watchInterviewRecord() {
        if (this.interviewRecord && this.interviewRecord.content) {
            this.content = this.interviewRecord.content;
            this.defaultValue = new Date(this.interviewRecord.interviewDate);
        }
    }
    ////////////////////////////////////////////////////////////////////
    saveInterviewRecord() {
        const interviewRecordInfo: InterviewRecordInfo = {
            content: this.content,
            interviewDate: this.interviewTime,
            appointmentId: +this.$route.params.appointmentId
            appointmentId: +this.appointmentId
        };
        if (!this.interviewId) {
            this.createdRecord(interviewRecordInfo);
        } else {
            const updateInterviewRecord = {
                ...interviewRecordInfo,
                id: +this.interviewId
        }
            this.updateRecord(updateInterviewRecord);
        }
    }
    private createdRecord(interviewRecordInfo) {
        appointmentService.createInterviewRecord(interviewRecordInfo).then(res => {
            this.isConfirmPopup = true;
            this.confirmTxt = '新增成功'
            this.showPopUp();
        });
    }
    closeConfirmPopup() {
        this.isConfirmPopup = false;
        this.$router.go(-1);
    private updateRecord(updateInterviewRecord) {
        appointmentService.updateInterviewRecord(updateInterviewRecord).then(res => {
            this.confirmTxt = '編輯成功';
            this.showPopUp();
        });
    }
    private showPopUp() {
        if (new Date(this.interviewTime).getTime() >= new Date().getTime()) {
            this.showFutureDateConfirmPopup = true;
        } else {
            this.showConfirmPopup = true;
        }
    }
    closePopup() {
        this.$router.push(`/appointment/${this.appointmentId}`);
    }
    deleteInterviewRecord() {
        appointmentService.deleteInterviewRecord(this.interviewId).then(res => {
            this.confirmTxt = '刪除成功';
            this.showConfirmPopup = true;
        });
    }
    cancel() {
        if (this.interviewId) {
           this.content = this.interviewRecord.content;
           this.defaultValue = new Date(this.interviewRecord.interviewDate);
           this.isEdit = false;
        } else {
           this.$router.push(`/appointment/${this.appointmentId}`);
        }
    }
    ////////////////////////////////////////////////////////////////////
    get formatInterviewDate() {
        const interviewDate = new Date(this.interviewRecord.interviewDate);
        return `${interviewDate.getFullYear()}/${interviewDate.getMonth() + 1}/${interviewDate.getDate()} ${interviewDate.getHours()}:${interviewDate.getMinutes()}`;
    }
}
PAMapp/components/Ui/UiDatePicker.vue
@@ -14,15 +14,26 @@
</template>
<script lang="ts">
import { Component, Emit, Vue } from "nuxt-property-decorator";
import { Component, Emit, Prop, Vue, Watch } from "nuxt-property-decorator";
@Component
export default class UiDatePicker extends Vue {
    dateValue = '';
    @Prop()
    defaultValue!: string;
    @Emit('changeDate')
    changeDate() {
        return this.dateValue;
    }
    @Watch('defaultValue', {immediate: true})
    updateDefault() {
        if (this.defaultValue) {
            this.dateValue = this.defaultValue;
            this.changeDate();
        }
    }
}
</script>
PAMapp/components/Ui/UiTimePicker.vue
@@ -14,7 +14,7 @@
</template>
<script lang="ts">
import { Component, Emit, Vue } from "nuxt-property-decorator";
import { Component, Emit, Prop, Vue, Watch } from "nuxt-property-decorator";
@Component
export default class UiTimePicker extends Vue {
@@ -25,9 +25,22 @@
        end: '21:00'
    }
    @Prop()
    defaultValue!: string;
    @Emit('changeTime')
    changeTime() {
        return this.timeValue;
    }
    @Watch('defaultValue', {immediate: true})
    updateDefault() {
        if (this.defaultValue) {
            const hours = new Date(this.defaultValue).getHours();
            const minutes = new Date(this.defaultValue).getMinutes();
            this.timeValue = `${hours < 10 ? '0' + hours : hours}:${minutes < 10 ? '0' + minutes : minutes}`;
            this.changeTime();
        }
    }
}
</script>
PAMapp/pages/appointment/_appointmentId/index.vue
@@ -80,7 +80,7 @@
    <section class="mt-30">
      <AppointmentInterviewList />
      <AppointmentInterviewList :interviewList="appointmentDetail.interviewRecordDTOs" />
    </section>
    <section class="mt-30">
PAMapp/shared/models/appointment.model.ts
@@ -74,7 +74,7 @@
  gender              : string;
  hopeContactTime     : string;
  id                  : number;
  interviewRecordDTOs : string[];
  interviewRecordDTOs : InterviewRecord[];
  job                 : string;
  lastModifiedDate    : string;
  name                : string;
PAMapp/shared/services/appointment.service.ts
@@ -77,7 +77,7 @@
  // 刪除約訪記錄
  async deleteInterviewRecord(interviewRecordId) {
    return http.delete(`/interview_record/'${interviewRecordId}`);
    return http.delete(`/interview_record/${interviewRecordId}`);
  }
}
PAMapp/store/index.ts
@@ -9,8 +9,10 @@
import reviewsService from '~/shared/services/reviews.service';
import { Consultant } from '~/shared/models/consultant.model';
import { Appointment, AppointmentLog } from '~/shared/models/appointment.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';
@Module
export default class Store extends VuexModule {
    recommendList: Consultant[] = [];
@@ -21,6 +23,16 @@
    myNewAppointmentSum: number = 0;
    myAppointmentReviewLogList: AppointmentLog[] = [];
    interviewRecord: InterviewRecord = {
        appointmentId   : 0,
        content         : '',
        createdBy       : '',
        createdDate     : '',
        id              : 0,
        interviewDate   : '',
        lastModifiedBy  : '',
        lastModifiedDate: ''
    }
    get isUserLogin() {
        return this.context.getters['localStorage/isUserLogin'];
@@ -56,6 +68,25 @@
        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
    storeRecommendList() {
        queryConsultantService.getRecommendConsultantList().then(data => {