保誠-保戶業務員媒合平台
wayne
2021-11-19 e05f762bc8bd7e93db954a748bf15dd2dcb4b0d9
Merge remote-tracking branch 'origin/master'

修改20個檔案
新增3個檔案
927 ■■■■■ 已變更過的檔案
PAMapp/assets/pam-animation.css 9 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/scss/utilities/_heading.scss 11 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/scss/utilities/_utilities.scss 7 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/scss/vendors/elementUI/_dropdown.scss 27 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/ts/api/consultant.ts 23 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/ts/api/share.ts 16 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/ts/storageRequests.ts 10 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/BackActionBar.vue 16 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Client/ClientCard.vue 15 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Consultant-ques.vue 106 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/Consultant/ConsultantCard.vue 15 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/NavBar.vue 154 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/QuickFilter/QuickFilterSelector.vue 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/loading.vue 75 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/layouts/default.vue 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/nuxt.config.js 5 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/agentInfo/_agentNo.vue 6 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/consultantLogin/index.vue 13 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/login/index.vue 9 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/questionnaire/_agentNo.vue 348 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/recommendConsultant/index.vue 19 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/plugins/filters/date.filter.ts 8 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/store/localStorage.ts 31 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/pam-animation.css
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1,9 @@
.pam-fade-in-enter-active{
  transition: 0.5s ease-in;
}
.pam-fade-in-enter{
  opacity: 0;
}
.pam-fade-in-enter-to{
  opacity: 1;
}
PAMapp/assets/scss/utilities/_heading.scss
@@ -85,3 +85,14 @@
.fix-chrome-click--issue{
  -webkit-tap-highlight-color: transparent;
}
.hint{
  font-size: 16px;
  color:$PRIMARY_RED;
  font-weight: bold;
  padding:0 10px;
}
.icon-information {
  font-size: 18px;
  color: $ORANGE;
  cursor: pointer;
}
PAMapp/assets/scss/utilities/_utilities.scss
@@ -62,10 +62,17 @@
.pr-5{
  padding-right: 5px;
}
.pl-5{
  padding-left: 5px;
}
.pl-10 {
  padding-left: 10px;
}
.pl-20{
  padding-left: 20px;
}
.pam-paragraph {
  margin: 30px 0;
}
PAMapp/assets/scss/vendors/elementUI/_dropdown.scss
@@ -1,21 +1,32 @@
.pam-header__dropdown {
.el-dropdown-menu.pam-header__dropdown {
  top:39px !important;
  border-radius: 10px;
  box-shadow: 0 3px 6px $LIGHT_GREY;
  padding: 5px 26px 5px 20px;
  .pam-header__dropdown-item {
  .el-dropdown-menu__item{
    width: max-content;
    margin:15px 0px;
    cursor: pointer;
    color: $PRIMARY_BLACK;
    background-color: $PRIMARY_WHITE;
    &.pam-header__dropdown-divider {
      padding-top: 16px;
      width: calc(100% + 6px);
      border-top: 1px solid #CCCCCC;
    }
    &:first-child{
      padding-top: 0px;
    }
    &:focus {
      color: $PRIMARY_BLACK;
      background-color: $PRIMARY_WHITE;
    }
    &:not(.is-disabled):hover{
      color: $PRIMARY_BLACK;
      background-color: $PRIMARY_WHITE;
    }
  }
  .pam-header__dropdown-divider {
    padding-top: 16px;
    width: calc(100% + 6px);
    border-top: 1px solid #CCCCCC;
  }
  .popper__arrow {
    display: none;
    &::after {
@@ -24,7 +35,7 @@
  }
}
@include desktop{
  .pam-header__dropdown {
  .el-dropdown-menu.pam-header__dropdown {
    top: 78px !important;
  }
}
PAMapp/assets/ts/api/consultant.ts
@@ -120,16 +120,25 @@
    status: string
}
export interface AppointmentRequests {
  connectDevices: any[],
  hopeContactTime: any,
  email?: string,
  job?: string,
  otherJob?: string,
  gender?: string,
  myQuestion: any[],
  age?: string,
}
export interface AppointmentParams {
    phone: string,
    email: string,
    phone?: string,
    email?: string,
    contactType: string,
    gender: string,
    age: string,
    job: string,
    gender?: string,
    age?: string,
    job?: string,
    requirement: string,
    hopeContactTime: string,
    otherRequirement: string,
    hopeContactTime?: string,
    agentNo: string
}
export interface StrictQueryParams{
PAMapp/assets/ts/api/share.ts
@@ -5,25 +5,27 @@
})
service.interceptors.request.use(function (config: AxiosRequestConfig) {
    loadingStart;
  loadingStart();
    return config;
}, function (error: AxiosError) {
    loadingFinish;
  loadingFinish();
    return Promise.reject(error);
});
service.interceptors.response.use(function (response: AxiosResponse) {
    loadingFinish;
  loadingFinish();
    return response;
}, function (error: AxiosError) {
    loadingFinish;
  loadingFinish();
    return Promise.reject(error);
});
function loadingStart():void{
function loadingStart(): void {
  setTimeout(() => {
    window.$nuxt.$loading.start();
  });
}
function loadingFinish():void{
function loadingFinish(): void {
    window.$nuxt.$loading.finish();
}
}
PAMapp/assets/ts/storageRequests.ts
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1,10 @@
import { AppointmentRequests } from "./api/consultant";
export function getRequestsFromStorage(): AppointmentRequests {
  const requests = localStorage.getItem('myRequests');
  return requests ? JSON.parse(requests) : null;
}
export function setRequestsToStorage(myRequests: AppointmentRequests): void {
  localStorage.setItem('myRequests', JSON.stringify(myRequests));
}
PAMapp/components/BackActionBar.vue
@@ -1,17 +1,20 @@
<template>
    <nav class="pam-back-action-bar fix-chrome-click--issue">
        <a @click="$router.push('/')">
        <a @click="pushRouterByLoginRole">
          <i class="icon-left "></i>{{ label }}
        </a>
    </nav>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import { namespace } from 'nuxt-property-decorator';
import { Vue, Component,} from 'vue-property-decorator';
import * as _ from 'lodash';
import { Role } from './NavBar.vue';
const localStorage = namespace('localStorage');
@Component
export default class UiCarousel extends Vue {
  @localStorage.Getter currentRole!:string;
  get label(): string {
    if (this.$route.name) {
      const routeName = this.$route.name.split('-')[0];
@@ -43,7 +46,10 @@
      return '回首頁';
    }
  }
  pushRouterByLoginRole():void{
    const link = _.isEqual(this.currentRole,Role.ADMIN)? '/myAppointmentList/appointmentList':'/';
    this.$router.push(link);
  }
}
</script>
PAMapp/components/Client/ClientCard.vue
@@ -54,8 +54,8 @@
                <p>性別:{{gender}}</p>
                <p>年齡:{{client.age}}</p>
                <p>職業:{{client.job}}</p>
                <p>需求:{{client.requirement.replace(',', '、')}}</p>
                <p v-for="(item, index) in hopeContactTime" :key="index">連絡時段{{index + 1 | formatNumber}}:{{item}}</p>
                <p>需求:{{client.requirement.split(',').join('、')}}</p>
                <p v-for="(item, index) in hopeContactTime" :key="index">連絡時段{{index + 1 | formatNumber}}:{{ item | formatHopeContactTime}}</p>
                </div>
                <div class="mt-30 text--center" v-if="isReserved">
                    <el-button @click="markAppointment">標註為已連絡</el-button>
@@ -76,6 +76,15 @@
                const upperNumber = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
                return upperNumber[index];
            }
        },
        formatHopeContactTime(item: string): string {
          if (item) {
            const [hopeDay, hopeTime] = item.split('、');
            const day = hopeDay.split(',').length > 6 ? '不限日期' : hopeDay;
            const time = hopeTime.split(',').length > 3 ? '不限時間' : hopeTime;
            return `${day}、${time}`;
          }
          return '';
        }
    }
})
@@ -205,4 +214,4 @@
    .text-right {
        text-align: right;
    }
</style>
</style>
PAMapp/components/Consultant-ques.vue
@@ -2,48 +2,64 @@
    <div>
        <div class="pam-tags">
            <div class="pb-10" v-for="(qaItem,index) in questionList" :key="index">
                <el-button  class="tags" :class="{'active': qaItem.selected}"
                @click="selectQuestion(index)">
                <el-button  class="tags" :class="{'active': qaItem.selected}"
                @click="selectQuestion(index)">
                {{qaItem.name}}
                </el-button>
            </div>
                </el-button>
            </div>
        </div>
    </div>
</template>
<script lang="ts">
import { Vue, Component, Emit} from 'vue-property-decorator';
import { Vue, Component, Emit, Prop} from 'vue-property-decorator';
@Component
    export default class ConsultantQues extends Vue{
        showDialog = false;
        other = '';
        otherQuestion = '';
        questionList=[
            {
                name:'健康與保障',
                selected:false
                },
            {
                name:'子女教育',
                selected:false
                },
            {
                name:'資產規劃',
                selected:false
                },
            {
                name:'樂活退休',
                selected:false
                },
            {
                name:'保單健檢/規劃',
                selected:false
                },
            {
                name:'防疫保單相關',
                selected:false
                }
        @Prop({ default: () => []}) selectedQuestions!: string[];
        get questionList(): any[] {
          return this.defaultQuestionList.map((question) => {
            const isSeleted = (quesName: string): boolean => this.selectedQuestions.some((question) => question === quesName);
            if (isSeleted(question.name)) {
                question.selected = true;
              }
            return question;
            }
          );
        }
        defaultQuestionList = [
          {
            name:'健康與保障',
            selected:false
          },
          {
            name:'子女教育',
            selected:false
          },
          {
            name:'資產規劃',
            selected:false
          },
          {
            name:'樂活退休',
            selected:false
          },
          {
            name:'保單健檢/規劃',
            selected:false
          },
          {
            name:'防疫保單相關',
            selected:false
          }
        ];
        @Emit('change')
@@ -57,22 +73,22 @@
<style lang="scss">
.con-input{
    input:focus,textarea:focus {
    outline: none;
    border: 1px solid #ED1B2E;
  }
}
}
}
.other-input{
    height: 50px;
    width:316px;
    border:1px solid #CCCCCC;
    margin-top: 10px;
}
.tags{
    height: 47px;
}
.qa-dialog-footer{
    display: flex;
    justify-content: center;
@@ -110,14 +126,14 @@
@include desktop
{
    .pam-tags{
        display:flex;
        flex-wrap:wrap;
    }
    .tags{
        margin-right: 10px;
        height: 47px;
    }
  .pam-tags{
      display:flex;
      flex-wrap:wrap;
  }
  .tags{
      margin-right: 10px;
      height: 47px;
  }
}
</style>
</style>
PAMapp/components/Consultant/ConsultantCard.vue
@@ -58,11 +58,11 @@
                    <p>性別:{{gender}}</p>
                    <p>年齡:{{appointmentDetail.age}}</p>
                    <p>職業:{{appointmentDetail.job}}</p>
                    <p>需求:{{appointmentDetail.requirement.replace(',', '、')}}</p>
                    <p>需求:{{appointmentDetail.requirement.split(',').join('、')}}</p>
                    <p
                        v-for="(item, index) in hopeContactTime"
                        :key="index"
                    >連絡時段{{index + 1 | formatNumber}}:{{item}}</p>
                    >連絡時段{{index + 1 | formatNumber}}:{{ item | formatHopeContactTime }}</p>
                </div>
            </div>
        </Ui-Dialog>
@@ -83,6 +83,15 @@
                const upperNumber = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
                return upperNumber[index];
            }
        },
        formatHopeContactTime(item: string): string {
          if (item) {
            const [hopeDay, hopeTime] = item.split('、');
            const day = hopeDay.split(',').length > 6 ? '不限日期' : hopeDay;
            const time = hopeTime.split(',').length > 3 ? '不限時間' : hopeTime;
            return `${day}、${time}`;
          }
          return '';
        }
    }
})
@@ -258,4 +267,4 @@
        height: 400px;
    }
</style>
</style>
PAMapp/components/NavBar.vue
@@ -1,93 +1,103 @@
<template>
  <header class="pam-header">
    <div class="pam-header__logo" @click="$router.push('/')"></div>
    <div class="pam-header__logo"
      @click="pushRouterByLoginRole"></div>
    <div class="pam-header__title">
      <div class="pam-header__title--main">我的幸福我做主</div>
      <div class="pam-header__title--sub">預約我的幸福守護者</div>
    </div>
    <div class="pam-header__action-bar" style="position:relaitive">
    <div class="pam-header__action-bar">
      <i class="icon-bell text--dark-blue cursor--pointer fix-chrome-click--issue"
        @click="$router.push('/notification')"></i>
      <el-dropdown>
        <i class="icon-avatar text--dark-blue cursor--pointer fix-chrome-click--issue"></i>
        <el-dropdown-menu slot="dropdown"
          class="pam-header__dropdown">
          <template v-for="(navbarItem,index) in navBarList">
            <li class="pam-header__dropdown-item fix-chrome-click--issue "
              :class="{'pam-header__dropdown-divider':navbarItem.title === '顧問登入'}"
              v-if="navbarItem.needRole.includes(loginRole)"
              :key="index"
              @click="linkTo(navbarItem.link)">
              {{navbarItem.title}}
            </li>
          </template>
        </el-dropdown-menu>
      </el-dropdown>
        <el-dropdown :class="{'is-open':isOpenDropdown}"
          ref="dropdown"
          trigger="click"
          @command="routerNavigateTo">
          <i class="icon-avatar text--dark-blue cursor--pointer fix-chrome-click--issue" @click="isOpenDropdown =!isOpenDropdown" @blur="isOpenDropdown =false"></i>
          <el-dropdown-menu
            class="pam-header__dropdown">
            <template v-for="(item,index) in navBarList">
              <el-dropdown-item :key="index"
                v-if="item.authorityOfRoleList.includes(loginRole)"
                class="fix-chrome-click--issue"
                :class="{'pam-header__dropdown-divider':item.title === '顧問登入'}"
                :command="item.routeUrl">
                {{item.title}}
              </el-dropdown-item>
            </template>
          </el-dropdown-menu>
        </el-dropdown>
    </div>
  </header>
</template>
<script lang="ts">
  import { Vue, Component } from 'vue-property-decorator';
  import { namespace } from 'nuxt-property-decorator';
  import * as _ from 'lodash';
  const localStorage = namespace('localStorage');
  @Component
  export default class NavBar extends Vue {
    @localStorage.Mutation storageClear!: () => void;
    @localStorage.Getter idToken!: string | null;
    @localStorage.Getter currentRole!: string | null;
    navBarList = [{
        needRole: [Role.NOT_LOGIN],
        link: '/login',
        authorityOfRoleList: [Role.NOT_LOGIN],
        routeUrl: '/login',
        title: '登入',
      },
      {
        needRole: [Role.USER],
        link: '/accountSetting',
        authorityOfRoleList: [Role.USER],
        routeUrl: '/accountSetting',
        title: '個人帳號設定',
      },
      {
        needRole: [Role.ADMIN],
        link: '/notFinish',
        authorityOfRoleList: [Role.ADMIN],
        routeUrl: '/notFinish',
        title: '查看帳號資訊',
      },
      {
        needRole: [Role.USER, Role.ADMIN],
        link: '/record/contactRecord',
        authorityOfRoleList: [Role.USER, Role.ADMIN],
        routeUrl: '/record/contactRecord',
        title: '查看紀錄',
      },
      {
        needRole: [Role.NOT_LOGIN, Role.USER],
        link: '/myConsultantList/consultantList',
        authorityOfRoleList: [Role.NOT_LOGIN, Role.USER],
        routeUrl: '/myConsultantList/consultantList',
        title: '我的顧問清單',
      },
      {
        needRole: [Role.USER, Role.ADMIN],
        link: '',
        authorityOfRoleList: [Role.USER, Role.ADMIN],
        routeUrl: '',
        title: '登出',
      },
      {
        needRole: [Role.NOT_LOGIN],
        link: '/consultantLogin',
        authorityOfRoleList: [Role.NOT_LOGIN],
        routeUrl: '/consultantLogin',
        title: '顧問登入',
      },
    ];
    get idToken(): string | null {
      return localStorage.getItem('id_token');
    }
    get roleOfState(): string | null {
      return localStorage.getItem('roleOfState');
    }
    login_role = Role.NOT_LOGIN;
    isOpenDropdown = false;
    get loginRole(): string {
      return this.roleOfState && this.idToken ? this.roleOfState : Role.NOT_LOGIN;
      return this.idToken && this.currentRole ? this.currentRole : Role.NOT_LOGIN;
    }
    linkTo(routerLink: string): void {
      _.isEqual(routerLink, '') ? this.fakeLogout() : this.$router.push(routerLink);
    routerNavigateTo(url: string): void {
      (this.$refs.dropdown as any).hide();
      _.isEqual(url, '') ? this.fakeLogout() : this.$router.push(url);
    }
    pushRouterByLoginRole(): void {
      const link = _.isEqual(this.currentRole, Role.ADMIN) ? '/myAppointmentList/appointmentList' : '/';
      this.$router.push(link);
    }
    // TODO: åƒ…OTP認證開發前 æš«æ™‚使用
    fakeLogout(): void {
      localStorage.clear();
      // this.$router.go(0);
      window.location.href = '/'
      this.storageClear();
      _.isEqual(this.$route.name, 'index') ? location.reload() : this.$router.push('/');
    }
  }
  export enum Role {
@@ -114,26 +124,30 @@
      width: 115px;
      margin: 0 10px;
      background-image: url('~/assets/images/taiwan-logo.png');
      background-repeat:no-repeat;
      background-repeat: no-repeat;
      background-size: contain;
      background-position: center;
    }
    .pam-header__title {
      flex: 1;
      flex-basis: 160px;
      border-left:1px #CCCCCC solid;
      padding-left:10px;
      .pam-header__title--main {
        font-size: 16px;
        font-weight: bold;
        color: $PRUDENTIAL_GREY;
        letter-spacing:0.8px;
        @media screen and (max-width: 352px) {
          font-size: 12px;
        }
      border-left: 1px #CCCCCC solid;
      padding-left: 10px;
    .pam-header__title--main {
      font-size: 16px;
      font-weight: bold;
      color: $PRUDENTIAL_GREY;
      letter-spacing: 0.8px;
      @media screen and (max-width: 352px) {
        font-size: 12px;
      }
      .pam-header__title--sub {
        padding-top: 2px;
    }
    .pam-header__title--sub {
      padding-top: 2px;
        font-size: 12px;
        transform: scale(0.9);
        -webkit-transform-origin-x: 0;
@@ -145,13 +159,17 @@
    .pam-header__action-bar {
      position: relative;
      display: flex;
      height: 100%;
      font-size: 24px;
      font-weight: bold;
      align-items: center;
      justify-content: space-around;
      i {
        padding: 0px 15px;
        @media screen and (max-width: 352px) {
          padding: 0px 10px;
        }
@@ -159,29 +177,45 @@
    }
  }
  .el-dropdown{
    height: 100%;
    display: flex;
    align-items: center;
    &.is-open {
        background-color: $PEACH;
      i {
        color: $PRIMARY_WHITE;
      }
    }
  }
  @include desktop {
    .pam-header {
      height: $DESKTOP_NAV_BAR;
      .pam-header__logo {
        width: 180px;
        height: 100%;
        margin: 0;
        background-image: url('~/assets/images/logo.png');
        background-size: cover;
        background-repeat:no-repeat;
        background-repeat: no-repeat;
        background-position: center;
      }
      .pam-header__title {
        display: flex;
        justify-content: start;
        align-items: center;
        border: none;
        padding-left: 30px;
        .pam-header__title--main{
        .pam-header__title--main {
          font-size: 30px;
          letter-spacing: 1.5px;
        }
        .pam-header__title--sub{
        .pam-header__title--sub {
          font-size: 20px;
          letter-spacing: 2px;
          transform: none;
PAMapp/components/QuickFilter/QuickFilterSelector.vue
@@ -7,7 +7,7 @@
            <span
                class="smTxt_bold text--primary"
                v-if="questionOption.name === 'communicationStyles'"
            >可複選</span>
            >(可複選)</span>
            <span
                class="smTxt_bold text--primary"
                v-if="questionOption.name === 'avgScore'"
PAMapp/components/loading.vue
@@ -1,42 +1,47 @@
<template>
    <div class="pam-loading" v-if="isLoading">
        <lottie
        :options="defaultOptions"
        :width="250"
        :height="250"
        :loop="true"/>
    </div>
  <div class="pam-loading"
    v-if="isLoading">
    <lottie :options="defaultOptions"
      :width="250"
      :height="250"
      :loop="true" />
  </div>
</template>
<script lang="ts">
    import { Component, Vue } from 'nuxt-property-decorator';
    import Lottie from 'vue-lottie/src/lottie.vue';
    @Component({
        components:{
            'lottie':Lottie
        }
    })
    export default class Loading extends Vue {
        isLoading=false;
        defaultOptions={ animationData: require('@/assets/lottie/loading.json')};
        start():void{
            this.isLoading = true;
        }
        finish():void{
            this.isLoading = false;
        }
  import { Component, Vue } from 'nuxt-property-decorator';
  import Lottie from 'vue-lottie/src/lottie.vue';
  @Component({
    components: {
      'lottie': Lottie
    }
  })
  export default class Loading extends Vue {
    isLoading = false;
    defaultOptions = {
      animationData: require('@/assets/lottie/loading.json')
    };
    start(): void {
      this.isLoading = true;
    }
    finish(): void {
      this.isLoading = false;
    }
  }
</script>
<style lang="scss" scoped>
    .pam-loading{
        position: absolute;
        background-color: rgba(#222222,0.5);
        width: 100%;
        height: 100%;
        z-index: 99;
        display: flex;
        justify-content: center;
        align-items: center;
    }
</style>
<style lang="scss"
  scoped>
  .pam-loading {
    position: absolute;
    background-color: rgba(#222222, 0.5);
    width: 100%;
    height: 100%;
    z-index: 99;
    display: flex;
    justify-content: center;
    align-items: center;
  }
</style>
PAMapp/layouts/default.vue
@@ -14,7 +14,7 @@
    </div>
    <div
      class="pam-container"
      :class="{containClassName, 'mt-navBar': bannerClassName === 'pam-no-banner'}"
      :class="[containClassName,{'mt-navBar': bannerClassName === 'pam-no-banner'}]"
    >
      <Nuxt class="pam-page-container"></Nuxt>
    </div>
PAMapp/nuxt.config.js
@@ -21,15 +21,16 @@
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },
  layoutTransition:'pam-fade-in',
  pageTransition:'pam-fade-in',
  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [
    'element-ui/lib/theme-chalk/index.css',
    'swiper/css/swiper.css',
    'vue-scroll-picker/dist/style.css',
    '~/assets/scss/main.scss',
    '~/assets/pam-animation.css'
  ],
  // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: [
    '~/plugins/element-ui.js',
PAMapp/pages/agentInfo/_agentNo.vue
@@ -67,7 +67,7 @@
              <div class="pam-field__title">
                <i class="pam-icon icon-puzzle"
                  ></i>匹配度
                  <i class="text--primary icon-information" @click="alertFieldInfo('suitability')"></i>
                  <i class="pl-5 text--primary icon-information" @click="alertFieldInfo('suitability')"></i>
              </div>
            </div>
            <div class="xsTxt">
@@ -93,7 +93,7 @@
            <div>
              <div class="pam-field__title">
                <i class="pam-icon icon-thumbs-up"
                  ></i>諮詢度表現 <i class="text--primary icon-information" @click="alertFieldInfo('evaluation')"></i>
                  ></i>諮詢度表現 <i class="pl-5 text--primary icon-information" @click="alertFieldInfo('evaluation')"></i>
              </div>
            </div>
            <div class="xsTxt">
@@ -268,6 +268,8 @@
    .pam-field__title {
      font-size: 16px;
      font-weight: bold;
      display: flex;
      align-items: center;
    }
  }
}
PAMapp/pages/consultantLogin/index.vue
@@ -63,13 +63,19 @@
</template>
<script lang="ts">
  import { namespace } from 'nuxt-property-decorator';
  import { Vue, Component} from 'vue-property-decorator';
  import { getForgotPasswordLink , getVerificationCodeImg , login } from '~/assets/ts/api/consultant';
  import { Role } from '../../components/NavBar.vue';
  const localStorage = namespace('localStorage');
  @Component({
    layout: 'default'
    layout: 'home'
  })
  export default class ConsultantLogin extends Vue {
    @localStorage.Mutation storageIdToken!: (token:string) => void;
    @localStorage.Mutation storageRole!: (role:string) => void;
    isRemember = false;
    isShowPassword = false;
    consultantDto = {
@@ -113,10 +119,9 @@
            username: 'admin',
            password: 'admin'
        }
        console.log('user',user);
        login(user).then((res) => {
            localStorage.setItem('id_token', res.data.id_token);
            localStorage.setItem('roleOfState',Role.ADMIN)
            this.storageIdToken(res.data.id_token);
            this.storageRole(Role.ADMIN);
            this.$router.push('/myAppointmentList/appointmentList');
        })
    }
PAMapp/pages/login/index.vue
@@ -220,11 +220,16 @@
</template>
<script lang="ts">
import { namespace } from 'nuxt-property-decorator';
import { Vue, Component } from 'vue-property-decorator';
import { login } from '~/assets/ts/api/consultant';
import { Role } from '../../components/NavBar.vue';
const localStorage = namespace('localStorage');
@Component
export default class Login extends Vue {
  @localStorage.Mutation storageIdToken!: (token:string) => void;
  @localStorage.Mutation storageRole!: (role:string) => void;
  connectDevice: 'MOBILE' | 'EMAIL' = 'MOBILE';
  phoneNumber = '';
@@ -268,8 +273,8 @@
            password: 'user',
        }
        login(user).then((res) => {
            localStorage.setItem('id_token', res.data.id_token);
            localStorage.setItem('roleOfState',Role.USER);
            this.storageIdToken(res.data.id_token);
            this.storageRole(Role.USER);
            this.$router.go(-1);
        })
  };
PAMapp/pages/questionnaire/_agentNo.vue
@@ -4,56 +4,81 @@
            <span class="mdTxt">在預約之前,請幫助我們填寫以下問題(<i style="color: #FF0000">*</i>必填)</span>
        </div>
        <div class="pb-18">
            <span class="mdTxt  required">請問您希望保險顧問以電話或Email聯繫您呢?</span>
            <span class="mdTxt required">請問您希望保險顧問以電話或Email聯繫您呢?</span>
            <span class="hint">可複選</span>
        </div>
        <div class="pam-tags">
            <el-button :class="{ 'active': isConnectMobile}" @click="toggleConnectDevice('mobile')" class="contactMax Btn">手機</el-button>
            <el-button :class="{ 'active': isConnectEmail}" @click="toggleConnectDevice('email')" class="contactMax Btn">Email</el-button>
            <el-button
             class="contactMax Btn"
              :class="{ 'active': isConnectMobile }"
              @click="toggleConnectDevice('mobile')"
            >
              æ‰‹æ©Ÿ
            </el-button>
            <el-button
              class="contactMax Btn"
              :class="{ 'active': isConnectEmail}"
              @click="toggleConnectDevice('email')"
            >
              Email
            </el-button>
        </div>
        <div class="connectDesktop">
            <div v-if="isConnectMobile">
                <span class="fz-20 phone-no">0912345678</span>
                <span class="fz-20 phone-no">
                  0912345678
                </span>
            </div>
            <el-row class="pb-10 pt-10">
                <el-input class="pam-input" v-if="isConnectEmail" placeholder="請輸入電子郵件信箱" v-model="email">
                <el-input
                  v-if="isConnectEmail"
                  class="pam-input"
                  placeholder="請輸入電子郵件信箱"
                  v-model="myRequest.email">
                </el-input>
            </el-row>
            <div v-if="isConnectMobile">
                <div class="datepicker">
                    <span class="mdTxt required">手機連絡的方便時間</span>
                    <PhoneContactTimePicker :scheduleList.sync="initScheduleList"/>
                    <PhoneContactTimePicker
                      :scheduleList.sync="myRequest.hopeContactTime"/>
                </div>
            </div>
        </div>
        <div class="mb-30">
            <div>
            <div class="pb-10 mt-10 mdTxt required">想要詢問的問題
            <span class="hint text--bold" @click="showDrawer = true">
            <i class="icon-information text--bold" @click="showDrawer = true"></i>可複選</span></div>
            <ConsultantQues @change="selectedQuestion = $event" class="mb-30"/>
            <div class="pb-10 mt-10 mdTxt required">
                æƒ³è¦è©¢å•çš„問題
                <span class="hint text--bold">
                    (可複選)
                </span>
                <i class="icon-information text--bold" @click="showDrawer = true"></i>
            </div>
              <ConsultantQues
                @change="myRequest.myQuestion = $event"
                :selectedQuestions="myRequest.myQuestion"
                class="mb-30"/>
            </div>
        </div>
        <div>
            <div class="pb-10 mdTxt pt-10">您的性別</div>
            <div class="mb-30 pam-tags">
                <el-button class="desktopBtn tags" @click="gender = 'male'" :class="{ 'active': gender ==='male'}">男性</el-button>
                <el-button class="desktopBtn tags" @click="gender = 'female'" :class="{'active': gender === 'female'}">女性</el-button>
                <el-button class="desktopBtn tags" @click="myRequest.gender = 'male'" :class="{ 'active': myRequest.gender ==='male'}">男性</el-button>
                <el-button class="desktopBtn tags" @click="myRequest.gender = 'female'" :class="{'active': myRequest.gender === 'female'}">女性</el-button>
            </div>
        </div>
        <div class="mdTxt pb-10">年齡</div>
        <div class=" pam-age-tags">
        <div class="pam-age-tags">
            <div>
                <el-button class="ageDesktop" @click="age = '20'" :class="{'active': age === '20'}">20歲以下</el-button>
                <el-button class="ageDesktop" @click="age = '30'" :class="{'active': age === '30'}">21-30æ­²</el-button>
                <el-button class="ageDesktopP2" @click="age = '40'" :class="{'active': age === '40'}">31-40æ­²</el-button>
                <el-button class="ageDesktop" @click="age = '50'" :class="{'active': age === '50'}">41-50æ­²</el-button>
                <el-button class="ageDesktopP2" @click="age = '55'" :class="{'active': age === '55'}">46-55æ­²</el-button>
                <el-button class="ageDesktop " @click="age = '60'" :class="{'active': age === '60'}">51-60æ­²</el-button>
                <el-button class="ageDesktopP2" @click="age = '70'" :class="{'active': age === '70'}">61-70æ­²</el-button>
                <el-button class="mb-10" @click="age = '71'" :class="{'active': age === '71'}">71歲以上</el-button>
                <el-button class="ageDesktop" @click="myRequest.age = '20'" :class="{'active': myRequest.age === '20'}">20歲以下</el-button>
                <el-button class="ageDesktop" @click="myRequest.age = '30'" :class="{'active': myRequest.age === '30'}">21-30æ­²</el-button>
                <el-button class="ageDesktopP2" @click="myRequest.age = '40'" :class="{'active': myRequest.age === '40'}">31-40æ­²</el-button>
                <el-button class="ageDesktop" @click="myRequest.age = '50'" :class="{'active': myRequest.age === '50'}">41-50æ­²</el-button>
                <el-button class="ageDesktopP2" @click="myRequest.age = '55'" :class="{'active': myRequest.age === '55'}">46-55æ­²</el-button>
                <el-button class="ageDesktop " @click="myRequest.age = '60'" :class="{'active': myRequest.age === '60'}">51-60æ­²</el-button>
                <el-button class="ageDesktopP2" @click="myRequest.age = '70'" :class="{'active': myRequest.age === '70'}">61-70æ­²</el-button>
                <el-button class="mb-10" @click="myRequest.age = '71'" :class="{'active': myRequest.age === '71'}">71歲以上</el-button>
            </div>
        </div>
@@ -61,16 +86,22 @@
        <div class="job-pick" @click="showJobDrawer = true">
            <input class="fz-20 pl-18 input"
                style="outline:none margin-right:-18px"
                @click="showJobDrawer = true" placeholder="請選擇" :value="staff === '其他' ? `其他, ${inputValue}` : staff">
            <i class="icon-down down-icon " style="margin-right:18px" @click="showJobDrawer = true" ></i>
                @click="showJobDrawer = true"
                placeholder="請選擇"
                :value="myRequest.job === '其他'
                        ? `其他, ${ myRequest.otherJob }`
                        : myRequest.job"
            >
              <i class="icon-down down-icon" style="margin-right:18px" @click="showJobDrawer = true" ></i>
        </div>
        <div class="ques-footer">
            <el-button type="primary"
            :disabled=" isInitScheduleDisabled || !isSelected"
            @click.native="sentDemand">送出</el-button>
          <el-button type="primary"
            :disabled="isInitScheduleDisabled || !isSelectedQues"
            @click.native="sentDemand"
          >
            é€å‡º
          </el-button>
        </div>
        <PopUpFrame :isOpen.sync="showDrawer" :drawerSize=" '95%' ">
@@ -78,7 +109,7 @@
                <div class="qa-dialog">
                    <div v-for="(qaText,index) in queaAboutList" :key="index" >
                        <div class="pt-10">
                            <p class=" p bold">{{qaText.title}}</p>
                            <p class="p bold">{{qaText.title}}</p>
                            <p class="p">{{qaText.content}}</p>
                        </div>
                    </div>
@@ -87,21 +118,23 @@
                <el-button type="primary" @click="showDrawer = false">我知道了</el-button>
            </div>
        </PopUpFrame>
        <PopUpFrame :isOpen.sync="showJobDrawer" drawerSize='60%'>
            <div class="job-drawerTxt fz-20">
                <div class="subTitle mt-18">職業</div>
                <div class="radio-btn">
                    <el-radio-group  class="pam-radio-group--col" v-model="staff">
                    <el-radio-group  class="pam-radio-group--col" v-model="myRequest.job">
                        <el-radio-button style="margin-top:30px" text-color='#F09491' label="外勤"></el-radio-button>
                        <el-radio-button style="margin-top:30px" label="內勤"></el-radio-button>
                        <el-radio-button style="margin-top:30px" label="其他"></el-radio-button>
                    </el-radio-group>
                </div>
                <div class="job-inputDiv"><input v-if="staff === '其他'" class="job-input mb-30 fz-20 pl-20" v-model="inputValue" > </div>
                <div class="job-inputDiv"><input v-if="myRequest.job === '其他'" class="job-input mb-30 fz-20 pl-20" v-model="myRequest.otherJob" > </div>
                <el-button type="primary" class="job-drawerBtn" @click="showJobDrawer = false">確定</el-button>
            </div>
        </PopUpFrame>
        <PopUpFrame :isOpen.sync="sendReserve" @update:isOpen="closeReservePopUp">
            <div class="fz-20 mt-30 sendReserve-txt">預約成功!您預約的保險顧問會</div>
            <div class="fz-20 sendReserve-txt">儘速與您聯絡!</div>
@@ -115,159 +148,155 @@
    </div>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import { appointmentDemand } from '~/assets/ts/api/consultant';
import { appointmentDemand, AppointmentParams, AppointmentRequests } from '~/assets/ts/api/consultant';
import { getRequestsFromStorage, setRequestsToStorage } from '~/assets/ts/storageRequests';
@Component
    export default class Questionnaire extends Vue {
                gender = '';
                connectDevices = new Array();
                mobileNumber = '';
                email = '';
                inputValue='';
                selectedQuestion: SelectedQuestion[] = [];
                staff = '';
                myRequest: AppointmentRequests = {
                  connectDevices: [],
                  hopeContactTime: [{
                    selectWeekOptions:[],
                    selectTimesOptions:[],
                  }],
                  gender: '',
                  email: '',
                  job: '',
                  otherJob: '',
                  myQuestion: [],
                  age: '',
                };
                age = '';
                showDrawer= false;
                showJobDrawer = false;
                sendReserve = false;
                initScheduleList=[{
                    selectWeekOptions:[],
                    selectTimesOptions:[],
                    }]
                queaAboutList=[
                    {
                        title:'健康與保障',
                        content:'突發的意外或疾病,往往造成個人或家庭沉重的經濟負擔,周全的保險才能擁有一個無憂的未來。'
                    },
                    {
                        title:'子女教育',
                        content:'利用分紅保單,規劃教育基金 ææ—©ç‚ºå­å¥³ä½œæº–備,讓生活更有保障!'
                    },
                    {
                        title:'資產規劃',
                        content:'當財務責任加重時,規劃充足的保障、提供經濟上的庇護,是人生最堅強的後盾。'
                    },
                    {
                        title:'樂活退休',
                        content:'兼具保險與投資雙重功能,可靈活搭配各種附約,順應人生不同階段的靈活需要。'
                    },
                    {
                        title:'保單健檢/規劃',
                        content:'全面檢視自己的保障結構是否符合現在或未來的風險移轉需求,透過「斷、捨、離」把錢花在刀口上。'
                    },
                    {
                        title:'防疫保單',
                        content:'匹配度是透過嚴選配對或快速篩選後,將每一位保險顧問資料進行比對後排序推薦給您的媒合數值,您可以作為選擇適合顧問的參考值。'
                    },
                    {
                        title:'其他',
                        content:'匹配度是透過嚴選配對或快速篩選後,將每一位保險顧問資料進行比對後排序推薦給您的媒合數值,您可以作為選擇適合顧問的參考值。'
                    },
                ]
                queaAboutList = [
                  {
                      title:'健康與保障',
                      content:'突發的意外或疾病,往往造成個人或家庭沉重的經濟負擔,周全的保險才能擁有一個無憂的未來。'
                  },
                  {
                      title:'子女教育',
                      content:'利用分紅保單,規劃教育基金 ææ—©ç‚ºå­å¥³ä½œæº–備,讓生活更有保障!'
                  },
                  {
                      title:'資產規劃',
                      content:'當財務責任加重時,規劃充足的保障、提供經濟上的庇護,是人生最堅強的後盾。'
                  },
                  {
                      title:'樂活退休',
                      content:'兼具保險與投資雙重功能,可靈活搭配各種附約,順應人生不同階段的靈活需要。'
                  },
                  {
                      title:'保單健檢/規劃',
                      content:'全面檢視自己的保障結構是否符合現在或未來的風險移轉需求,透過「斷、捨、離」把錢花在刀口上。'
                  },
                  {
                      title:'防疫保單',
                      content:'匹配度是透過嚴選配對或快速篩選後,將每一位保險顧問資料進行比對後排序推薦給您的媒合數值,您可以作為選擇適合顧問的參考值。'
                  }
                ];
                agentNo!: string;
                get disableActionButton(): boolean {
                    return true;
                };
                get isConnectMobile(): boolean {
                    return this.connectDevices.includes('mobile');
                    return this.myRequest.connectDevices.includes('mobile');
                }
                get isConnectEmail(): boolean {
                    return this.connectDevices.includes('email');
                    return this.myRequest.connectDevices.includes('email');
                }
                get isSelectedQues(): boolean {
                  return !!this.myRequest.myQuestion.length;
                }
                get isInitScheduleDisabled() {
                  if (this.isConnectMobile && this.isConnectEmail) {
                      return !this.myRequest?.hopeContactTime[0]?.selectWeekOptions?.length
                          || !this.myRequest?.hopeContactTime[0]?.selectTimesOptions?.length
                          || !this.myRequest.email
                  } else if (this.isConnectMobile) {
                      return !this.myRequest?.hopeContactTime[0]?.selectWeekOptions?.length
                        || !this.myRequest?.hopeContactTime[0]?.selectTimesOptions?.length
                  } else if (this.isConnectEmail) {
                      return !this.myRequest.email
                  }
                  return true;
                }
                mounted() {
                    this.agentNo = this.$route.params.agentNo;
                }
                get isSelected() {
                    return this.selectedQuestion.findIndex(i => i.selected)>=0
                    const storageMyRequest = getRequestsFromStorage();
                    if (storageMyRequest) {
                      this.myRequest = storageMyRequest;
                    }
                }
                toggleConnectDevice(selectDevice: 'mobile' | 'email'): void {
                    const deviceSelected = this.connectDevices.includes(selectDevice);
                    const deviceSelected = this.myRequest.connectDevices.includes(selectDevice);
                    if (deviceSelected) {
                        const deviceIndex = this.connectDevices.findIndex((device) => device === selectDevice);
                        this.connectDevices.splice(deviceIndex, 1);
                        const deviceIndex = this.myRequest.connectDevices.findIndex((device) => device === selectDevice);
                        this.myRequest.connectDevices.splice(deviceIndex, 1);
                        if (selectDevice === 'mobile') {
                            this.initScheduleList = [{
                            this.myRequest.hopeContactTime = [{
                                selectWeekOptions:[],
                                selectTimesOptions:[],
                            }]
                        }
                        if (selectDevice === 'email') {
                            this.email = '';
                            this.myRequest.email = '';
                        }
                        return;
                    }
                    this.connectDevices.push(selectDevice);
                    this.myRequest.connectDevices.push(selectDevice);
                }
                sentDemand() {
                    const data = {
                    const data: AppointmentParams = {
                        phone: '09123456789',
                        email: this.email,
                        contactType: this.connectDevices.toString(),
                        gender: this.gender,
                        age: this.age,
                        job: this.staff,
                        requirement: this.getRequirement(),
                        email: this.myRequest.email,
                        contactType: this.myRequest.connectDevices.toString(),
                        gender: this.myRequest.gender,
                        age: this.myRequest.age,
                        job: this.myRequest.job !== '其他' ? this.myRequest.job : this.myRequest.otherJob,
                        requirement: this.myRequest.myQuestion
                                      .filter((ques) => ques.selected)
                                      .map((selQues) => selQues.name).toString(),
                        hopeContactTime: this.getHopeContactTime(),
                        otherRequirement: '',
                        agentNo: this.agentNo
                    }
                    appointmentDemand(data).then(res => {
                        this.sendReserve = true
                    })
                }
                    };
                getRequirement() {
                    const requirement = this.selectedQuestion.filter(item => item.selected)
                    return requirement.map(item => item.name).toString();
                    appointmentDemand(data).then(res => {
                        this.sendReserve = true;
                        console.log('sendMyReq', this.myRequest.myQuestion);
                        this.myRequest.myQuestion = this.myRequest.myQuestion
                                      .filter((ques) => ques.selected)
                                      .map((selQues) => selQues.name);
                        setRequestsToStorage(this.myRequest);
                    });
                }
                getHopeContactTime() {
                    const isFullDay = (selectedDay: string[]): boolean => selectedDay.length > 6;
                    const isFullTime = (selectedTime: string[]): boolean => selectedTime.length > 3;
                    const initScheduleList = this.initScheduleList.map(item => {
                        return {
                            selectWeekOptions: isFullDay(item.selectWeekOptions) ? '不限日期' : item.selectWeekOptions.toString(),
                            selectTimesOptions: isFullTime(item.selectTimesOptions) ? '不限時間' : item.selectTimesOptions.toString()
                        }
                    })
                    return initScheduleList.map(i => {
                    return this.myRequest.hopeContactTime.map(i => {
                        return `'${i.selectWeekOptions}、${i.selectTimesOptions}'`}
                    ).toString()
                    ).toString();
                }
                closeReservePopUp() {
                    this.sendReserve = false;
                    this.$router.push('/')
                }
                get isInitScheduleDisabled() {
                    console.log(this.isConnectMobile)
                    if (this.isConnectMobile && this.isConnectEmail) {
                        return !this.initScheduleList[0].selectWeekOptions.length || !this.initScheduleList[0].selectTimesOptions.length || !this.email
                    } else if (this.isConnectMobile) {
                        return !this.initScheduleList[0].selectWeekOptions.length || !this.initScheduleList[0].selectTimesOptions.length
                    } else if (this.isConnectEmail) {
                        return !this.email
                    }
                    return true;
                }
    }
@@ -276,6 +305,7 @@
        selected: boolean;
    }
</script>
<style lang="scss" scoped>
.input{
    border:none;
@@ -302,43 +332,38 @@
    margin-right: 30px;
}
.job-drawerTxt{
    input:focus,textarea:focus {
    outline: none;
    border: 1px solid #ED1B2E;
  input:focus,textarea:focus {
  outline: none;
  border: 1px solid #ED1B2E;
  }
}
}
input:focus,textarea:focus {
    outline: none;
    border: 1px solid #FFFFFF;
}
.sendReserve-txt{
    display: flex;
    justify-content: center;
    margin-top: 10px;
    margin-bottom: 26px;
}
.pl-20{
    padding-left: 20px;
}
.job-inputDiv{
    height:90px;
}
.jobBtn{
    font-weight:bold;
}//職業選項,按了變粗體
.job-input{
    height: 50px;
    width: 294px;
    border: 1px solid #FF0000;
}//職業選項,其他輸入框樣式
.job-pick{
    height: 50px;
    border-radius:10px;
@@ -355,9 +380,11 @@
    color: #ED1B2E;
    cursor: pointer;
}//drawer最底下文字樣式
.mt-18{
    margin-top: 18px;
}
.job-drawerTxt{
    display: flex;
    flex-direction: column;
@@ -385,8 +412,9 @@
    border-style: solid;
    pointer-events: none;
    }
    }
  }
}//職業選項內所有文字排版與按鈕樣式設定
.ques-footer{
    justify-content: center;
    margin: 30px 0;
@@ -418,19 +446,23 @@
    }
    }
}//送出按鈕樣式與排版
.qa-dialog{
    overflow-y:auto;
    height: 500px;
    margin-top: 20px;
}//詳細問題drawer中間內容空間大小設置
.qaTextTitle{
    margin-top:30px;
    display: flex;
    justify-content: center;
}//詳細問題drawer主要標題
.el-drawer__container ::-webkit-scrollbar {
        display: none;
    }
}
.phone-no{
    margin-bottom: 30px;
    margin-left: 5px;
@@ -444,6 +476,7 @@
.el-button+.el-button{
    margin-left: 0;
}
.down-icon{
        color:#ED1B2E;
        font-size: 25px;
@@ -451,27 +484,31 @@
        justify-content: flex-end;
        padding-top: 11px;
        margin-left: -20px;
    }
}
.job-txt{
    align-self: center;
    margin-left: 18px;
    color:#D0D0CE;
}
.date-txt{
    align-self: center;
    margin-left: 18px;
    color: #68737A;
}
.add-date{
    color:#D0D0CE;
    padding-top: 10px;
    padding-bottom: 30px;
    font-size: 20px;
}
.pb-16{
    padding-bottom: 16px;
}
.date-pick{
    height: 50px;
    border-radius:10px;
@@ -479,6 +516,7 @@
    display: flex;
    justify-content: space-between;
}
.date-icon{
    color:#ED1B2E;
        font-size: 25px;
@@ -487,24 +525,30 @@
        padding-right: 16px;
        padding-top: 11px;
}
.addDate{
    margin-left: -20px;
}
.ageTags{
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
}
.datepicker{
    display: flex;
    flex-direction: column;
}
.pb-18{
    padding-bottom: 18px;
}
.mr-20{
    margin-right: 20px;
}
.pam-age-tags {
    display: flex;
    .el-button {
@@ -526,7 +570,7 @@
        background-color: #F09491;
        color: white;
    }
    }
  }
}
.required {
@@ -539,14 +583,6 @@
    }
}
.hint {
    font-size: 16px;
    color: #ED1B2E;
    font-weight: bold;
    .icon-information {
        padding: 0 5px;
    }
}
@include desktop {
    .contactMax{
PAMapp/pages/recommendConsultant/index.vue
@@ -21,8 +21,9 @@
      <div class="pb-10 mdTxt required">
        æƒ³è¦è©¢å•çš„問題
        <span class="hint text--bold">
          <i class="icon-information text--bold fix-chrome-click--issue" @click="showDialog = true"></i>可複選
          (可複選)
        </span>
        <i class="icon-information text--bold fix-chrome-click--issue" @click="showDialog = true"></i>
      </div>
      <MultiSelectBtn :mutiSelect.sync="strictQueryDto.requirements" :options="requirementOptions" class="rec-multi-select" />
    </div>
@@ -38,7 +39,7 @@
    <div class="pam-paragraph">
      <div class="rec-popular">
        <div class="pb-10 mdTxt">熱門檢索</div>
        <div class="hint text--bold ml-10">可複選</div>
        <div class="hint text--bold ml-10">(可複選)</div>
      </div>
      <div class="rec-pop-container">
      <MultiSelectBtn :mutiSelect.sync="strictQueryDto.popularTags"
@@ -485,19 +486,7 @@
      z-index: 5;
    }
  }
  .hint {
    font-size: 16px;
    color: #ED1B2E;
    font-weight: bold;
    .icon-information {
      padding: 0 5px;
      cursor: pointer;
    }
  }
  .area-txt {
    display: flex;
    align-items: center;
PAMapp/plugins/filters/date.filter.ts
@@ -24,12 +24,14 @@
    return compareDate.getFullYear() === today.getFullYear();
  };
  const minutes = date.getMinutes() > 10 ?  date.getMinutes() : `0${date.getMinutes()}`;
  if (isThisYear(date)) {
    return isToday(date)
          ? `今天 ${date.getHours()}:${date.getMinutes()}`
          : `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${date.getMinutes()}`;
          ? `今天 ${date.getHours()}:${minutes}`
          : `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${minutes}`;
  } else {
    return `${date.getFullYear()}å¹´${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${date.getMinutes()}`;
    return `${date.getFullYear()}å¹´${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${minutes}`;
  };
})
PAMapp/store/localStorage.ts
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1,31 @@
import { Module, Mutation, VuexModule } from 'vuex-module-decorators';
@Module
export default class LocalStorage extends VuexModule {
  id_token:string|null = null;
  role_State:string|null = null;
  get idToken(): string|null {
    return this.id_token;
  };
  get currentRole(): string|null {
    return this.role_State;
  };
  @Mutation storageIdToken(token: string): void {
    localStorage.setItem('id_token', token);
    this.id_token = localStorage.getItem('id_token') ;
  };
  @Mutation storageRole(role:string): void {
    localStorage.setItem('current_role', role);
    this.role_State = localStorage.getItem('current_role');
  };
  @Mutation storageClear(): void {
    localStorage.clear();
    this.id_token = localStorage.getItem('id_token');
    this.role_State = localStorage.getItem('roleOfState');
  }
}