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')" > </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'); } }