保誠-保戶業務員媒合平台
[ Update ] : 1.nav bar 下拉選單UI 微調,將localStorage存入store 監聽角色來去改變下拉選單對應項目  2. 顧問登入layout 更改 3.layout page 新增淡入特效 ,
修改8個檔案
新增2個檔案
250 ■■■■ 已變更過的檔案
PAMapp/assets/pam-animation.css 9 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/scss/vendors/elementUI/_dropdown.scss 27 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/assets/ts/api/share.ts 10 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/BackActionBar.vue 16 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/NavBar.vue 115 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/components/loading.vue 15 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/nuxt.config.js 5 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/consultantLogin/index.vue 13 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
PAMapp/pages/login/index.vue 9 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
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/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;
    &:first-child{
      padding-top: 0px;
    }
  }
  .pam-header__dropdown-divider {
    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;
    }
  }
  .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/share.ts
@@ -5,23 +5,25 @@
})
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{
  setTimeout(() => {
    window.$nuxt.$loading.start();
  });
}
function loadingFinish():void{
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/NavBar.vue
@@ -1,25 +1,29 @@
<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"
        <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" ></i>
          <el-dropdown-menu
          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 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>
@@ -29,65 +33,72 @@
<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.isOpenDropdown = !this.isOpenDropdown;
      (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 {
@@ -118,20 +129,24 @@
      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;
        }
      }
      .pam-header__title--sub {
        padding-top: 2px;
        font-size: 12px;
@@ -145,13 +160,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,9 +178,22 @@
    }
  }
  .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%;
@@ -171,16 +203,19 @@
        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{
          font-size: 30px;
          letter-spacing: 1.5px;
        }
        .pam-header__title--sub{
          font-size: 20px;
          letter-spacing: 2px;
PAMapp/components/loading.vue
@@ -1,7 +1,7 @@
<template>
    <div class="pam-loading" v-if="isLoading">
        <lottie
        :options="defaultOptions"
  <div class="pam-loading"
    v-if="isLoading">
    <lottie :options="defaultOptions"
        :width="250"
        :height="250"
        :loop="true"/>
@@ -18,7 +18,9 @@
    })
    export default class Loading extends Vue {
        isLoading=false;
        defaultOptions={ animationData: require('@/assets/lottie/loading.json')};
    defaultOptions = {
      animationData: require('@/assets/lottie/loading.json')
    };
        start():void{
            this.isLoading = true;
        }
@@ -26,9 +28,11 @@
            this.isLoading = false;
        }
    }
</script>
<style lang="scss" scoped>
<style lang="scss"
  scoped>
    .pam-loading{
        position: absolute;
        background-color: rgba(#222222,0.5);
@@ -39,4 +43,5 @@
        justify-content: center;
        align-items: center;
    }
</style>
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/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/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');
  }
}