StepOne.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. <template>
  2. <div class="step-1 ptc-wrapper">
  3. <div class="ptc-block">
  4. <div class="ptc-inner">
  5. <p class="ptc-label">Choose a version</p>
  6. <PtcRadioGroup
  7. v-model="state.form.product_id"
  8. class="version-list"
  9. :class="{ loose: state.productList.length === 2 }"
  10. >
  11. <div
  12. v-for="product of state.productList"
  13. :key="product.id"
  14. class="version-wrap"
  15. >
  16. <PtcRadio
  17. class="version"
  18. :value="product.id"
  19. :disabled="!!state.form.renewal"
  20. :style="{ fontSize: calcFZ(product.name) }"
  21. >
  22. <i
  23. class="version-icon"
  24. :style="{ backgroundImage: `url(${product.image})` }"
  25. ></i>
  26. <p class="version-name ellipsis-2">{{ product.name }}</p>
  27. </PtcRadio>
  28. </div>
  29. </PtcRadioGroup>
  30. <div
  31. class="version-desc"
  32. :class="{
  33. second:
  34. state.productList[1] &&
  35. state.form.product_id === state.productList[1].id,
  36. }"
  37. >
  38. {{ getters.selectedProduct.remark }}
  39. </div>
  40. </div>
  41. </div>
  42. <div class="ptc-block">
  43. <div class="ptc-inner">
  44. <p class="ptc-label">Choose a subscription method</p>
  45. <PtcRadioGroup v-model="state.form.subscribe_type">
  46. <PtcRadio class="method" value="year">
  47. <span class="cost">${{ getters.selectedProduct.amount_year }}</span>
  48. <span class="name">Annual</span>
  49. <span class="ptc-tag"
  50. >-{{ getters.selectedProduct.better_than_monthly }}% OFF</span
  51. >
  52. </PtcRadio>
  53. <PtcRadio class="method" value="month">
  54. <span class="cost"
  55. >${{ getters.selectedProduct.amount_month }}</span
  56. >
  57. <span class="name">
  58. Monthly<template v-if="+getters.selectedProduct.amount_month_open"
  59. >&nbsp; Activation fee ${{
  60. getters.selectedProduct.amount_month_open
  61. }}</template
  62. ></span
  63. >
  64. </PtcRadio>
  65. </PtcRadioGroup>
  66. </div>
  67. </div>
  68. <div class="ptc-block">
  69. <div class="ptc-inner">
  70. <p class="ptc-label">Do you have a discount code?</p>
  71. <div v-if="!state.discount" class="input-wrap pr">
  72. <input
  73. v-model="state.form.discount_code"
  74. class="ptc-input"
  75. placeholder="Enter promotional code"
  76. />
  77. <button class="input-btn" @click="checkDiscount">sbumit</button>
  78. </div>
  79. <div v-else class="coupon-wrap">
  80. <div class="coupon">
  81. <p class="p1">PTC CARE PLUS</p>
  82. <p class="p2">{{ state.discount.name }}</p>
  83. <p class="p3">- ${{ state.discount.discount_amount }}</p>
  84. </div>
  85. <div class="action">
  86. <span class="code">{{ state.form.discount_code }}</span>
  87. <span class="primary" @click="reviseDiscount">Revise</span>
  88. </div>
  89. </div>
  90. </div>
  91. </div>
  92. <div class="total">
  93. <div v-if="getters.cost" class="ptc-inner">
  94. <p>
  95. total<strong>${{ getters.cost }}</strong>
  96. </p>
  97. <p v-if="state.discount" class="highlight">
  98. (${{ state.discount.discount_amount }} discounted)
  99. </p>
  100. </div>
  101. </div>
  102. <div class="ptc-button-group">
  103. <div class="ptc-inner">
  104. <button
  105. class="ptc-button"
  106. :class="{ disabled: !state.form.subscribe_type }"
  107. @click="next"
  108. >
  109. NEXT
  110. </button>
  111. </div>
  112. </div>
  113. </div>
  114. </template>
  115. <script setup lang="ts">
  116. import { PtcRadioGroup, PtcRadio } from '@/components/radio'
  117. import { state, getters, getBrandList } from './store'
  118. import * as api from '@/service/order'
  119. import Toast from '@/components/toast'
  120. const emit = defineEmits<{
  121. (e: 'go', delta?: number): void
  122. }>()
  123. async function checkDiscount() {
  124. if (!state.form.discount_code) return Toast('Please enter promotional code')
  125. try {
  126. const { results, message } = await api.checkDiscount(
  127. state.form.discount_code
  128. )
  129. Toast(message)
  130. state.discount = results
  131. } catch {
  132. state.form.discount_code = ''
  133. }
  134. }
  135. function reviseDiscount() {
  136. state.discount = null
  137. state.form.discount_code = ''
  138. }
  139. function calcFZ(text: string) {
  140. const scale = Math.max(Math.min(5 / text.length, 1), 24 / 56)
  141. return 0.56 * scale + 'rem'
  142. }
  143. async function next() {
  144. if (!state.form.subscribe_type)
  145. return Toast('Please choose a subscription method')
  146. if (!state.brandList.length) await getBrandList()
  147. emit('go')
  148. if (!state.discount) state.form.discount_code = ''
  149. }
  150. </script>
  151. <style lang="scss" scoped>
  152. .version-list {
  153. display: flex;
  154. flex-wrap: wrap;
  155. }
  156. .version-wrap {
  157. display: flex;
  158. margin-bottom: 32px;
  159. width: 33.33%;
  160. &:nth-child(3n - 1) {
  161. justify-content: center;
  162. }
  163. &:nth-child(3n) {
  164. justify-content: flex-end;
  165. }
  166. .loose & {
  167. width: 256px;
  168. }
  169. }
  170. .version {
  171. display: flex;
  172. flex-direction: column;
  173. justify-content: center;
  174. align-items: center;
  175. width: 212px;
  176. height: 212px;
  177. font-size: 56px;
  178. font-weight: bold;
  179. &:nth-child(3n) {
  180. margin-right: 0;
  181. }
  182. &-icon {
  183. width: 36px;
  184. height: 36px;
  185. background-repeat: no-repeat;
  186. background-size: contain;
  187. }
  188. &-name {
  189. margin-top: 18px;
  190. padding: 0 8px;
  191. text-align: center;
  192. }
  193. &-desc {
  194. position: relative;
  195. // margin-top: 32px;
  196. padding: 28px 24px;
  197. line-height: 40px;
  198. font-size: 28px;
  199. color: #193059;
  200. background: #f2f5fb;
  201. // &::before {
  202. // content: '';
  203. // position: absolute;
  204. // left: 88px;
  205. // top: 0;
  206. // transform: translateY(-100%);
  207. // border-style: solid;
  208. // border-width: 0 12px 14px;
  209. // border-color: #f2f5fb transparent;
  210. // }
  211. // &.second::before {
  212. // left: 88px + 276px;
  213. // }
  214. }
  215. }
  216. .method {
  217. display: flex;
  218. align-items: center;
  219. padding: 0 38px;
  220. height: 130px;
  221. + .method {
  222. margin-top: 48px;
  223. }
  224. .cost {
  225. font-size: 56px;
  226. font-weight: bold;
  227. }
  228. .name {
  229. margin-left: 20px;
  230. font-size: 32px;
  231. }
  232. }
  233. .ptc-input {
  234. padding-right: 154px;
  235. }
  236. .input-btn {
  237. position: absolute;
  238. top: 0;
  239. right: 0;
  240. height: 100%;
  241. width: 154px;
  242. background: #1a3059;
  243. border-radius: 0px 8px 8px 0px;
  244. color: #fff;
  245. font-size: 32px;
  246. &:active {
  247. background: $primary-color-lighten;
  248. }
  249. }
  250. .total {
  251. padding-left: 36px;
  252. font-size: 32px;
  253. color: #333;
  254. strong {
  255. margin-left: 8px;
  256. font-size: 40px;
  257. }
  258. }
  259. .coupon {
  260. @include icon('@img/coupon-s.png', 488px, 224px);
  261. margin-bottom: 48px;
  262. padding-top: 22px;
  263. text-align: center;
  264. font-size: 28px;
  265. font-weight: bold;
  266. .p1 {
  267. color: #9aa8c5;
  268. }
  269. .p2 {
  270. margin: 16px 0 34px;
  271. color: $primary-color;
  272. }
  273. .p3 {
  274. font-size: 56px;
  275. color: $primary-color;
  276. }
  277. }
  278. .action {
  279. font-size: 32px;
  280. color: #666;
  281. .code {
  282. margin-right: 64px;
  283. }
  284. }
  285. .highlight {
  286. margin-top: 4px;
  287. color: $danger-color;
  288. }
  289. </style>