index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. <template>
  2. <div :class="['nav-bar', { 'nav-bar--fixed': fixed }]">
  3. <div class="nav-bar-left">
  4. <div class="nav-icons">
  5. <i v-if="showNavIcons" class="icon-menu" @click="showMenu = true"></i>
  6. <router-link class="icon-logo" to="/"></router-link>
  7. <i v-if="showNavIcons" class="icon-mine" @click.stop="onClickMine"></i>
  8. </div>
  9. <transition
  10. v-if="showNavIcons"
  11. enter-active-class="fadeIn"
  12. leave-active-class="fadeOut"
  13. >
  14. <div v-show="showMenu" class="nav-menu nav-links">
  15. <div class="nav-menu-header">
  16. <i class="logo"></i>
  17. <i class="close" @click="showMenu = false"></i>
  18. </div>
  19. <div class="nav-menu-body">
  20. <ul class="nav-links-inner">
  21. <li class="nav-link">
  22. <router-link to="/repaire/appointment"
  23. >REPAIR BOOKING</router-link
  24. >
  25. </li>
  26. <li class="nav-link">
  27. <router-link to="/fill-order">ONLINE SHOP</router-link>
  28. </li>
  29. <li class="nav-link">PTC Care Plus</li>
  30. <li class="nav-link">SOTRE LOCATION</li>
  31. <li class="nav-link">CONTACT US</li>
  32. </ul>
  33. </div>
  34. </div>
  35. </transition>
  36. </div>
  37. <div v-if="showNavIcons" class="nav-bar-right">
  38. <div class="user pointer flex-ac" @click.stop="onClickMine">
  39. <template v-if="state.userInfo">
  40. <i class="icon-mine"></i>
  41. <span class="user-name">{{
  42. state.userInfo ? state.userInfo.name : 'Sign in'
  43. }}</span>
  44. </template>
  45. <span v-else class="u-no">LOGIN</span>
  46. </div>
  47. <div class="contact">
  48. <i class="icon-phone"></i>
  49. <div>
  50. <p class="tel">400-100-100</p>
  51. <p class="time">Mon-Fri &nbsp;9:00-17:00</p>
  52. </div>
  53. </div>
  54. </div>
  55. <transition enter-active-class="fadeIn" leave-active-class="fadeOut">
  56. <div
  57. v-if="state.userInfo"
  58. v-show="showMine"
  59. class="nav-menu nav-dropdown"
  60. >
  61. <div class="nav-menu-header">
  62. <i class="logo"></i>
  63. <i class="close" @click="showMine = false"></i>
  64. </div>
  65. <div
  66. class="nav-menu-body user-panel"
  67. :class="{ 'is-member': state.userInfo.orders.length }"
  68. >
  69. <div class="info" @click.stop>
  70. <div class="p1">
  71. <p class="name">Hi, {{ state.userInfo.name }}</p>
  72. <p class="intro">
  73. {{ state.userInfo.orders.length ? 'Pro' : 'Ordinary' }} member
  74. </p>
  75. </div>
  76. <div v-if="!state.userInfo.orders.length" class="p2">
  77. <p class="txt">You have not purchased a member</p>
  78. <button class="btn" @click="$router.push('/fill-order')">
  79. BUY
  80. </button>
  81. </div>
  82. <div v-else class="p3">
  83. <div class="txt">
  84. $10 off for additional services |
  85. <strong class="primary">Buy now ></strong>
  86. </div>
  87. <div class="swiper">
  88. <div class="swiper-wrapper">
  89. <div
  90. v-for="(item, index) of state.userInfo.orders"
  91. :key="index"
  92. class="swiper-slide"
  93. @click="$router.push(`/order/${item.id}`)"
  94. >
  95. <div class="service">
  96. <div class="service-title">
  97. <i class="service-icon icon-lite fls0"></i>
  98. <div class="service-type fls0">
  99. {{ item.product_name }}
  100. </div>
  101. <div class="service-period">
  102. {{ item.start_time }} to {{ item.end_time }}
  103. </div>
  104. </div>
  105. <div class="service-model tac">{{ item.phone_info }}</div>
  106. <div class="service-code tac">{{ item.phone_imei }}</div>
  107. </div>
  108. </div>
  109. </div>
  110. </div>
  111. <div v-if="state.userInfo.orders.length > 1" class="pagination">
  112. <i
  113. v-for="n of state.userInfo.orders.length"
  114. :key="n"
  115. :class="['dot', { active: slideIdx === n - 1 }]"
  116. ></i>
  117. </div>
  118. </div>
  119. </div>
  120. <div class="role">
  121. <router-link to="/dashboard">
  122. {{
  123. state.userInfo.orders.length
  124. ? 'Pro Member >'
  125. : 'Ordinary Member >'
  126. }}
  127. </router-link>
  128. </div>
  129. <ul class="dropdown-list">
  130. <template v-if="state.userInfo.orders.length">
  131. <li class="dropdown-item i1">
  132. <router-link to="/repaire/history"
  133. >MY REPAIR REQUEST</router-link
  134. >
  135. </li>
  136. <li class="dropdown-item i2">
  137. <router-link to="/order">MY ORDER</router-link>
  138. </li>
  139. </template>
  140. <li class="dropdown-item i3">
  141. <router-link to="/gift-card">MY DISCOUNT COUPON</router-link>
  142. </li>
  143. <li class="dropdown-item i4">
  144. <router-link to="/invite"
  145. >INVITE FRIENDS
  146. <span class="ptc-tag">Get a $10 coupon</span></router-link
  147. >
  148. </li>
  149. <li class="dropdown-item i5">
  150. <router-link to="/account">ACCOUNT INFORMATION</router-link>
  151. </li>
  152. <li class="dropdown-item i6" @click="signOut">SIGN OUT</li>
  153. </ul>
  154. </div>
  155. </div>
  156. </transition>
  157. </div>
  158. </template>
  159. <script setup lang="ts">
  160. import { ref, watch, onMounted } from 'vue'
  161. import { useRoute, useRouter } from 'vue-router'
  162. import { state, logout } from '@/store'
  163. import Dialog from '@/components/dialog'
  164. import Swiper from 'swiper'
  165. import 'swiper/css'
  166. import { lockScroll } from '@/utils/dom'
  167. interface Props {
  168. showNavIcons?: boolean
  169. fixed?: boolean
  170. }
  171. defineProps<Props>()
  172. const router = useRouter()
  173. const route = useRoute()
  174. const showMenu = ref(false)
  175. const showMine = ref(false)
  176. const slideIdx = ref(0)
  177. document.addEventListener('click', () => (showMine.value = false))
  178. onMounted(() => {
  179. new Swiper('.swiper', {
  180. slidesPerView: 1.08,
  181. spaceBetween: 15,
  182. on: {
  183. slideChange(swiper) {
  184. slideIdx.value = swiper.realIndex
  185. },
  186. },
  187. })
  188. })
  189. watch(
  190. () => route.fullPath,
  191. () => {
  192. showMenu.value = showMine.value = false
  193. }
  194. )
  195. watch(showMenu, toggleLock)
  196. watch(showMine, toggleLock)
  197. function toggleLock(show: boolean) {
  198. if (show) {
  199. window.innerWidth < 768 && lockScroll(true)
  200. } else {
  201. lockScroll(false)
  202. }
  203. }
  204. function onClickMine() {
  205. if (state.userInfo) {
  206. showMine.value = !showMine.value
  207. } else {
  208. router.push('/login')
  209. }
  210. }
  211. async function signOut() {
  212. await Dialog.confirm('TIPS', 'Are you sure you want to sign out?', {
  213. confirmText: 'YES',
  214. })
  215. await logout()
  216. router.push('/')
  217. }
  218. </script>
  219. <style lang="scss">
  220. :root {
  221. --nav-bar-height: 118px;
  222. @include media-breakpoint-up(md) {
  223. --nav-bar-height: 148px;
  224. }
  225. @include media-breakpoint-up(lg) {
  226. --nav-bar-height: 168px;
  227. }
  228. }
  229. .nav-bar {
  230. height: var(--nav-bar-height);
  231. background: #fff;
  232. border-bottom: 1px solid #d9d9d9;
  233. &--fixed {
  234. position: fixed;
  235. left: 0;
  236. top: 0;
  237. width: 100%;
  238. z-index: 10;
  239. }
  240. &-left {
  241. @include media-breakpoint-up(md) {
  242. display: flex;
  243. align-items: center;
  244. }
  245. }
  246. &-right {
  247. display: none;
  248. flex-shrink: 0;
  249. @include media-breakpoint-up(md) {
  250. display: flex;
  251. align-items: center;
  252. margin-left: 100px;
  253. .user .icon-mine {
  254. display: block;
  255. margin-right: 8px;
  256. width: 32px;
  257. height: 32px;
  258. }
  259. .user-name {
  260. font-size: 32px;
  261. font-weight: 600;
  262. color: $primary-color;
  263. }
  264. .u-no {
  265. font-size: 32px;
  266. color: #193059;
  267. }
  268. .contact {
  269. position: relative;
  270. display: none;
  271. margin-left: 30px;
  272. padding-left: 32px;
  273. &::before {
  274. content: '';
  275. position: absolute;
  276. left: 0;
  277. top: 50%;
  278. transform: translateY(-50%);
  279. width: 2px;
  280. height: 72px;
  281. background: #dae1ef;
  282. }
  283. }
  284. .tel {
  285. line-height: 44px;
  286. font-size: 32px;
  287. font-weight: 600;
  288. color: $primary-color;
  289. }
  290. .time {
  291. line-height: 44px;
  292. font-size: 32px;
  293. font-weight: 500;
  294. color: #999;
  295. }
  296. }
  297. @media (min-width: 1400px) {
  298. .contact {
  299. display: flex;
  300. align-items: center;
  301. }
  302. }
  303. }
  304. .nav-icons {
  305. box-sizing: content-box;
  306. position: relative;
  307. display: flex;
  308. justify-content: space-between;
  309. align-items: center;
  310. padding: 0 40px;
  311. height: var(--nav-bar-height);
  312. }
  313. .icon-menu {
  314. @include icon('@img/menu.png', 44px);
  315. }
  316. .icon-mine {
  317. @include icon('@img/user.png', 44px);
  318. }
  319. .icon-logo {
  320. @include icon('@img/logo.png', 130px, 70px);
  321. position: absolute;
  322. left: 50%;
  323. top: 50%;
  324. transform: translate(-50%, -50%);
  325. }
  326. .icon-phone {
  327. @include icon('@img/phone.png', 76px);
  328. margin-right: 26px;
  329. }
  330. @include media-breakpoint-up(md) {
  331. display: flex;
  332. justify-content: space-between;
  333. align-items: center;
  334. padding: 0 32px;
  335. .icon-menu,
  336. .icon-mine {
  337. display: none;
  338. }
  339. .icon-logo {
  340. position: static;
  341. margin-right: 32px;
  342. transform: none;
  343. }
  344. }
  345. @include media-breakpoint-up(lg) {
  346. padding: 0 120px;
  347. .icon-logo {
  348. margin-right: 126px;
  349. width: 207px;
  350. height: 99px;
  351. }
  352. }
  353. .nav-menu {
  354. display: flex;
  355. flex-direction: column;
  356. position: fixed;
  357. left: 0;
  358. right: 0;
  359. top: 0;
  360. bottom: 0;
  361. &-header {
  362. position: relative;
  363. display: flex;
  364. align-items: center;
  365. justify-content: center;
  366. height: var(--nav-bar-height);
  367. .logo {
  368. @include icon('@img/logo.png', 130px, 70px);
  369. }
  370. .close {
  371. @include icon('@img/close.png', 44px);
  372. position: absolute;
  373. top: 50%;
  374. transform: translateY(-50%);
  375. right: 40px;
  376. }
  377. }
  378. &-body {
  379. flex: 1;
  380. overflow: auto;
  381. }
  382. }
  383. .nav-links {
  384. background: $primary-color;
  385. @include media-breakpoint-up(md) {
  386. display: block !important;
  387. position: static;
  388. background: none;
  389. .nav-menu-header {
  390. display: none;
  391. }
  392. }
  393. .logo {
  394. background-image: url(@img/logo2.png);
  395. transform: scale(1.03);
  396. }
  397. .close {
  398. background-image: url(@img/close2.png);
  399. }
  400. }
  401. .nav-dropdown {
  402. background: #fff;
  403. @include media-breakpoint-up(md) {
  404. left: auto;
  405. right: 20px;
  406. bottom: auto;
  407. top: var(--nav-bar-height);
  408. margin-top: 2px;
  409. box-shadow: 0px 0px 28px 0px rgba(0, 0, 0, 0.08);
  410. z-index: 2;
  411. .nav-menu-header,
  412. .info,
  413. .ptc-tag {
  414. display: none;
  415. }
  416. .dropdown-list {
  417. margin-top: 0;
  418. }
  419. }
  420. @media (min-width: 1400px) {
  421. right: 520px;
  422. }
  423. }
  424. .nav-links-inner {
  425. margin-top: 36px;
  426. line-height: 144px;
  427. font-size: 40px;
  428. font-weight: 600;
  429. color: #fff;
  430. @include media-breakpoint-up(md) {
  431. display: flex;
  432. margin-top: 0;
  433. font-size: 28px;
  434. font-weight: 400;
  435. line-height: 44px;
  436. color: #193059;
  437. }
  438. @include media-breakpoint-up(lg) {
  439. font-size: 32px;
  440. }
  441. }
  442. .nav-link {
  443. padding-left: 196px;
  444. background-color: $primary-color;
  445. transition: background-color 0.3s ease;
  446. &:active {
  447. background-color: $primary-color-lighten;
  448. }
  449. @include media-breakpoint-up(md) {
  450. padding-left: 0;
  451. background-color: #fff !important;
  452. + .nav-link {
  453. margin-left: 48px;
  454. }
  455. }
  456. @include media-breakpoint-up(lg) {
  457. + .nav-link {
  458. margin-left: 64px;
  459. }
  460. }
  461. a {
  462. display: block;
  463. width: 100%;
  464. }
  465. }
  466. }
  467. .user-panel {
  468. .info {
  469. margin: 20px 36px 24px;
  470. @include media-breakpoint-up(md) {
  471. margin: 0 0 24px 0;
  472. }
  473. }
  474. .p1 {
  475. position: relative;
  476. padding: 48px 0 0 50px;
  477. height: 202px;
  478. color: $primary-color;
  479. background-color: #e9ebf0;
  480. border-radius: 8px 8px 0px 0px;
  481. overflow: hidden;
  482. &::after {
  483. content: '';
  484. @include icon('@img/user-lg.png', 190px);
  485. position: absolute;
  486. top: 46px;
  487. right: 42px;
  488. }
  489. .name {
  490. font-size: 48px;
  491. font-weight: 600;
  492. }
  493. .intro {
  494. margin-top: 16px;
  495. font-size: 32px;
  496. }
  497. }
  498. .p2 {
  499. padding: 48px;
  500. height: 224px;
  501. border-radius: 0 0 8px 8px;
  502. border: 2px solid #e9ebf0;
  503. .txt {
  504. line-height: 44px;
  505. font-size: 32px;
  506. color: #333;
  507. }
  508. .btn {
  509. display: block;
  510. margin-top: 24px;
  511. width: 144px;
  512. height: 60px;
  513. font-size: 32px;
  514. font-weight: 600;
  515. color: #fff;
  516. background: $primary-color;
  517. border-radius: 8px;
  518. &:active {
  519. background: $primary-color-lighten;
  520. }
  521. }
  522. }
  523. .p3 {
  524. padding-bottom: 32px;
  525. border-radius: 0 0 8px 8px;
  526. background: #e9ebf0;
  527. .txt {
  528. padding: 24px 36px 24px 48px;
  529. font-size: 30px;
  530. color: #333;
  531. }
  532. .swiper {
  533. padding-left: 44px;
  534. padding-right: 36px;
  535. }
  536. .service {
  537. padding: 26px 18px;
  538. // width: 526px;
  539. height: 266px;
  540. background: #ffffff;
  541. border-radius: 8px;
  542. &-title {
  543. display: flex;
  544. // align-items: center;
  545. font-size: 28px;
  546. }
  547. &-icon {
  548. margin-right: 10px;
  549. width: 36px;
  550. height: 36px;
  551. }
  552. &-type {
  553. margin-right: 30px;
  554. font-weight: bold;
  555. color: $primary-color;
  556. }
  557. &-period {
  558. color: #90a0c0;
  559. }
  560. &-model {
  561. margin-top: 48px;
  562. font-size: 40px;
  563. font-weight: 600;
  564. text-align: center;
  565. }
  566. &-code {
  567. margin-top: 8px;
  568. font-size: 28px;
  569. text-align: center;
  570. color: #999;
  571. }
  572. }
  573. .pagination {
  574. display: flex;
  575. justify-content: center;
  576. margin-top: 32px;
  577. .dot {
  578. width: 16px;
  579. height: 16px;
  580. border-radius: 50%;
  581. background: rgba(26, 48, 89, 0.3);
  582. &.active {
  583. background: $primary-color;
  584. }
  585. + .dot {
  586. margin-left: 24px;
  587. }
  588. }
  589. }
  590. }
  591. &.is-member {
  592. .p1,
  593. .role {
  594. color: #fff;
  595. background-color: $primary-color;
  596. }
  597. .p1::after {
  598. background-image: url(@img/pro-lg.png);
  599. top: 64px;
  600. }
  601. }
  602. .role {
  603. display: none;
  604. line-height: 80px;
  605. text-align: center;
  606. font-size: 32px;
  607. font-weight: 600;
  608. color: #193059;
  609. background-color: #e9ebf0;
  610. @include media-breakpoint-up(md) {
  611. display: block;
  612. }
  613. }
  614. .dropdown-list {
  615. margin-top: 24px;
  616. }
  617. .dropdown-item {
  618. position: relative;
  619. display: flex;
  620. align-items: center;
  621. padding-left: 144px;
  622. line-height: 144px;
  623. font-size: 32px;
  624. color: #1a1a1a;
  625. background-color: #fff;
  626. background-position: 84px center;
  627. background-repeat: no-repeat;
  628. background-size: 32px 32px;
  629. transition: background-color 0.3s ease;
  630. cursor: pointer;
  631. @include media-breakpoint-up(md) {
  632. padding-left: 80px;
  633. padding-right: 24px;
  634. background-position: 24px center;
  635. font-size: 28px;
  636. line-height: 124px;
  637. }
  638. &:hover,
  639. &:active {
  640. background-color: #f2f5fb;
  641. }
  642. &.i1 {
  643. background-image: url(@img/m-repair.png);
  644. }
  645. &.i2 {
  646. background-image: url(@img/m-order.png);
  647. }
  648. &.i3 {
  649. background-image: url(@img/m-coupon.png);
  650. }
  651. &.i4 {
  652. background-image: url(@img/m-friend.png);
  653. }
  654. &.i5 {
  655. background-image: url(@img/m-info.png);
  656. }
  657. &.i6 {
  658. background-image: url(@img/m-exit.png);
  659. }
  660. a {
  661. display: block;
  662. width: 100%;
  663. }
  664. }
  665. }
  666. </style>