Browse Source

fill-order step one

冯诚 2 years ago
parent
commit
8ebc74abbd

+ 2 - 1
.eslintrc.js

@@ -2,12 +2,13 @@ module.exports = {
   extends: ['ktsn-vue'],
 
   rules: {
+    'no-duplicate-imports': 'off',
     'no-unused-expressions': 'off',
     'no-empty': 'off',
     'no-control-regex': 'off',
     'vue/valid-template-root': 'off',
     'vue/require-default-prop': 'off',
     'vue/no-v-html': 'off',
-    '@typescript-eslint/no-empty-interface': 'off'
+    '@typescript-eslint/no-empty-interface': 'off',
   },
 }

+ 14 - 10
src/App.vue

@@ -1,12 +1,10 @@
 <template>
-  <div :class="{ 'has-navbar': showNavBar }">
-    <NavBar
-      v-if="showNavBar"
-      :show-nav-icons="$route.path !== '/password'"
-      :fixed="true"
-    />
-    <router-view :key="$route.fullPath" />
-  </div>
+  <NavBar
+    v-if="showNavBar"
+    :show-nav-icons="$route.path !== '/password'"
+    :fixed="true"
+  />
+  <router-view :key="$route.fullPath" class="page-container" />
 </template>
 
 <script setup lang="ts">
@@ -19,7 +17,13 @@ const showNavBar = computed(() => !navBarIgnore.includes(route.path))
 </script>
 
 <style lang="scss">
-.has-navbar {
-  padding-top: $nav-bar-height;
+.page-container {
+  min-height: 100vh;
+  padding-bottom: 48px;
+  overflow: hidden;
+
+  .nav-bar-wrap + & {
+    padding-top: $nav-bar-height;
+  }
 }
 </style>

BIN
src/assets/coupon-2.png


src/assets/coupon.png → src/assets/coupon-s.png


BIN
src/assets/coupon0.png


BIN
src/assets/coupon1.png


BIN
src/assets/coupon3.png


BIN
src/assets/coupon4.png


+ 1 - 12
src/components/nav-bar/index.vue

@@ -72,7 +72,7 @@
             <li class="item">MY ORDER</li>
             <li class="item">MY DISCOUNT COUPON</li>
             <li class="item">
-              INVITE FRIENDS <span class="tag">Get a $10 coupon</span>
+              INVITE FRIENDS <span class="ptc-tag">Get a $10 coupon</span>
             </li>
             <li class="item">ACCOUNT INFORMATION</li>
             <li class="item">SIGN OUT</li>
@@ -349,17 +349,6 @@ const showMine = ref(false)
     &:nth-child(6) {
       background-image: url(@img/m-exit.png);
     }
-
-    .tag {
-      margin-left: 32px;
-      padding: 0 24px;
-      line-height: 52px;
-      font-size: 28px;
-      font-weight: 600;
-      color: #fff;
-      background: $danger-color;
-      border-radius: 30px 4px 30px 4px;
-    }
   }
 
   .is-member {

+ 54 - 0
src/components/radio/Radio.vue

@@ -0,0 +1,54 @@
+<template>
+  <div
+    :class="['ptc-radio', { 'ptc-radio--checked': checked }]"
+    @click="onClick"
+  >
+    <slot />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { inject, computed } from 'vue'
+
+const props = defineProps<{ value: any; modelValue?: any }>()
+const emit = defineEmits<{
+  (e: 'update:modelValue', value: any): void
+}>()
+const parent = inject<any>('RadioGroup')
+
+const checked = computed(
+  () => props.value === (parent ? parent.modelValue.value : props.modelValue)
+)
+
+function onClick() {
+  if (checked.value) return
+  parent
+    ? parent.emit('update:modelValue', props.value)
+    : emit('update:modelValue', props.value)
+}
+</script>
+
+<style lang="scss">
+.ptc-radio {
+  position: relative;
+  border-radius: 8px;
+  border: 2px solid #d9d9d9;
+  color: #666;
+  &--checked {
+    border-color: $primary-color;
+    color: $primary-color;
+    &::after {
+      content: '';
+      position: absolute;
+      right: 0;
+      bottom: 0;
+      @include icon('@img/check.png', 68px, 72px);
+    }
+  }
+
+  &:not(.ptc-radio--checked) [class*='icon-'] {
+    filter: grayscale(1);
+    opacity: 0.65;
+  }
+}
+</style>

+ 17 - 0
src/components/radio/RadioGroup.vue

@@ -0,0 +1,17 @@
+<template>
+  <div class="ptc-radio-group"><slot /></div>
+</template>
+
+<script setup lang="ts">
+import { provide, toRef } from 'vue'
+
+const props = defineProps<{ modelValue: any }>()
+const emit = defineEmits<{
+  (e: 'update:modelValue', value: any): void
+}>()
+
+provide('RadioGroup', {
+  modelValue: toRef(props, 'modelValue'),
+  emit,
+})
+</script>

+ 2 - 0
src/components/radio/index.ts

@@ -0,0 +1,2 @@
+export { default as PtcRadioGroup } from './RadioGroup.vue'
+export { default as PtcRadio } from './Radio.vue'

+ 198 - 0
src/pages/fill-order/StepOne.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="step-one">
+    <div class="ptc-block">
+      <p class="ptc-label">Choose a version</p>
+      <PtcRadioGroup v-model="state.product" style="display: flex">
+        <PtcRadio class="version" value="lite">
+          <i class="icon-lite"></i>
+          <span class="mt18">Lite</span>
+        </PtcRadio>
+        <PtcRadio class="version" value="pro">
+          <i class="icon-pro"></i>
+          <span class="mt18">Pro</span>
+        </PtcRadio>
+      </PtcRadioGroup>
+      <div class="version-desc" :class="{ pro: state.product === 'pro' }">
+        Introduction of member products,Introduction of member products
+      </div>
+    </div>
+    <div class="ptc-block">
+      <p class="ptc-label">Choose a subscription method</p>
+      <PtcRadioGroup v-model="state.type">
+        <PtcRadio class="method" :value="1">
+          <span class="cost">$99</span>
+          <span class="name">Annual</span>
+          <span class="ptc-tag">-25% OFF</span>
+        </PtcRadio>
+        <PtcRadio class="method" :value="2">
+          <span class="cost">$12.5</span>
+          <span class="name"> Monthly&nbsp; Activation fee $20</span>
+        </PtcRadio>
+      </PtcRadioGroup>
+    </div>
+    <div class="ptc-block">
+      <p class="ptc-label">Do you have a discount code?</p>
+      <div v-if="!showCoupon" class="input-wrap pr">
+        <input
+          v-model="state.couponCode"
+          class="ptc-input"
+          placeholder="Enter promotional code"
+        />
+        <button class="input-btn" @click="showCoupon = true">sbumit</button>
+      </div>
+      <div v-else class="coupon-wrap">
+        <div class="coupon">
+          <p class="p1">PTC CARE PLUS</p>
+          <p class="p2">NEWCOMER DISCOUNT</p>
+          <p class="p3">- $10</p>
+        </div>
+        <div class="action">
+          <span class="code">87889122</span>
+          <span class="ptc-text" @click="showCoupon = false">Revise</span>
+        </div>
+      </div>
+    </div>
+
+    <div class="total">
+      <p>total<strong>$99</strong></p>
+      <p class="highlight">($10 discounted)</p>
+    </div>
+    <button class="ptc-button block-button">NEXT</button>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import { PtcRadioGroup, PtcRadio } from '@/components/radio'
+import { state } from './store'
+
+const showCoupon = ref(false)
+</script>
+
+<style lang="scss" scoped>
+.version {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  margin-right: 64px;
+  width: 212px;
+  height: 212px;
+  font-size: 56px;
+  font-weight: bold;
+
+  .mt18 {
+    margin-top: 18px;
+  }
+
+  &-desc {
+    position: relative;
+    margin-top: 32px;
+    padding: 28px 24px;
+    line-height: 40px;
+    font-size: 28px;
+    color: #193059;
+    background: #f2f5fb;
+    &::before {
+      content: '';
+      position: absolute;
+      left: 88px;
+      top: 0;
+      transform: translateY(-100%);
+      border-style: solid;
+      border-width: 0 12px 14px;
+      border-color: #f2f5fb transparent;
+    }
+    &.pro::before {
+      left: 88px + 276px;
+    }
+  }
+}
+
+.method {
+  display: flex;
+  align-items: center;
+  padding: 0 38px;
+  height: 130px;
+  + .method {
+    margin-top: 48px;
+  }
+
+  .cost {
+    font-size: 56px;
+    font-weight: bold;
+  }
+  .name {
+    margin-left: 20px;
+    font-size: 32px;
+  }
+}
+
+.ptc-input {
+  padding-right: 154px;
+}
+
+.input-btn {
+  position: absolute;
+  top: 0;
+  right: 0;
+  height: 100%;
+  width: 154px;
+  background: #1a3059;
+  border-radius: 0px 8px 8px 0px;
+  color: #fff;
+  font-size: 32px;
+  &:active {
+    background: $primary-color-lighten;
+  }
+}
+
+.total {
+  margin-bottom: 48px;
+  padding-left: 36px;
+  font-size: 32px;
+  color: #333;
+  strong {
+    margin-left: 8px;
+    font-size: 40px;
+  }
+}
+.block-button {
+  display: block;
+  margin: auto;
+  width: 678px;
+}
+
+.coupon {
+  @include icon('@img/coupon-s.png', 488px, 224px);
+  margin-bottom: 48px;
+  padding-top: 22px;
+  text-align: center;
+  font-size: 28px;
+  font-weight: bold;
+
+  .p1 {
+    color: #9aa8c5;
+  }
+  .p2 {
+    margin: 16px 0 34px;
+    color: $primary-color;
+  }
+  .p3 {
+    font-size: 56px;
+    color: $primary-color;
+  }
+}
+.action {
+  font-size: 32px;
+  color: #666;
+  .code {
+    margin-right: 64px;
+  }
+}
+
+.highlight {
+  margin-top: 4px;
+  color: $danger-color;
+}
+</style>

+ 16 - 0
src/pages/fill-order/index.vue

@@ -0,0 +1,16 @@
+<template>
+  <div class="p-fill-order">
+    <h3 class="ptc-title">Fill Order</h3>
+    <StepOne />
+  </div>
+</template>
+
+<script setup lang="ts">
+import StepOne from './StepOne.vue'
+</script>
+
+<style lang="scss">
+.p-fill-order {
+  background: #f7f7f7;
+}
+</style>

+ 13 - 0
src/pages/fill-order/store.ts

@@ -0,0 +1,13 @@
+import { reactive } from 'vue'
+
+interface State {
+  product: string
+  type: number
+  couponCode: string
+}
+
+export const state = reactive<State>({
+  product: '',
+  type: -1,
+  couponCode: '',
+})

+ 1 - 6
src/pages/imei/index.vue

@@ -12,9 +12,7 @@
         <span @click="$router.push('view')">How to view IMEI?</span>
       </p>
     </div>
-    <button class="ptc-button">
-      BIND IMEI
-    </button>
+    <button class="ptc-button">BIND IMEI</button>
   </div>
 
   <div v-else class="p-imei">
@@ -35,9 +33,6 @@ defineProps<{ action: 'bind' | 'view' }>()
 
 <style lang="scss">
 .p-imei {
-  display: flex;
-  flex-direction: column;
-  min-height: calc(100vh - $nav-bar-height);
   background: #f7f7f7;
 
   .title {

+ 4 - 0
src/router.ts

@@ -20,5 +20,9 @@ export default createRouter({
       component: () => import('./pages/imei/index.vue'),
       props: true,
     },
+    {
+      path: '/fill-order',
+      component: () => import('./pages/fill-order/index.vue'),
+    },
   ],
 })

+ 8 - 0
src/style/atom.scss

@@ -5,3 +5,11 @@
 .fw600 {
   font-weight: 600;
 }
+
+.pr {
+  position: relative;
+}
+
+.flex {
+  display: flex;
+}

+ 33 - 0
src/style/components.scss

@@ -37,3 +37,36 @@
   font-size: 32px;
   color: #333;
 }
+
+.ptc-title {
+  padding: 36px 24px;
+  line-height: 56px;
+  font-size: 40px;
+  font-weight: 600;
+  color: #333;
+}
+
+.ptc-block {
+  margin-bottom: 24px;
+  padding: 48px 36px;
+  background: #fff;
+}
+
+.ptc-label {
+  margin-bottom: 48px;
+  line-height: 44px;
+  font-size: 32px;
+  color: #999;
+}
+
+.ptc-tag {
+  display: inline-block;
+  margin-left: 32px;
+  padding: 0 24px;
+  line-height: 52px;
+  font-size: 28px;
+  font-weight: 600;
+  color: #fff;
+  background: $danger-color;
+  border-radius: 30px 4px 30px 4px;
+}

+ 1 - 2
src/style/login.scss

@@ -1,7 +1,6 @@
 .p-login,
 .p-register {
-  min-height: 100vh;
-  background: lightblue;
+  background: $primary-color;
   overflow: hidden;
 
   .logo {