鸿宇研学生前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
6.0 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <template>
  2. <view :class="['product', size]"
  3. @touchstart="onTouchstart"
  4. @touchmove="onTouchmove"
  5. @touchend="onTouchend"
  6. >
  7. <image class="product-img" :src="data.image" mode="aspectFill"></image>
  8. <view class="flex flex-column product-info">
  9. <view class="product-info-top">
  10. <view class="product-name text-ellipsis-2">{{ data.title }}</view>
  11. <view class="product-desc text-ellipsis" v-if="tagDesc">{{ tagDesc }}</view>
  12. </view>
  13. <view class="flex product-info-bottom">
  14. <view class="product-detail">
  15. <view class="flex product-price">
  16. <view class="product-price-val">
  17. <text>¥</text>
  18. <text class="highlight">{{ priceInt }}</text>
  19. <text>{{ `${priceFrac}` }}</text>
  20. </view>
  21. <view class="product-price-bef" v-if="data.priceOrigin">
  22. {{ `¥${data.priceOrigin}` }}
  23. </view>
  24. </view>
  25. <view class="product-registered">
  26. {{ `${data.applyNum}人已报名` }}
  27. </view>
  28. </view>
  29. <button class="btn" @click="onRegistrate">报名</button>
  30. </view>
  31. </view>
  32. <button class="flex btn btn-collect"
  33. :style="collectBtnStyle"
  34. @click.stop="onCollect"
  35. @touchstart.stop="onCollect"
  36. >
  37. <view>{{ isCollected ? '移除收藏' : '收藏' }}</view>
  38. </button>
  39. </view>
  40. </template>
  41. <script>
  42. export default {
  43. props: {
  44. data: {
  45. type: Object,
  46. default() {
  47. return {}
  48. }
  49. },
  50. size: {
  51. type: String,
  52. default: 'normal' // normal | small
  53. }
  54. },
  55. data() {
  56. return {
  57. isMove: false,
  58. startClientX: null,
  59. displayX: 0,
  60. collectBtnVisible: false,
  61. }
  62. },
  63. computed: {
  64. tagDesc() {
  65. // todo: check key
  66. const { tagList } = this.data
  67. return tagList?.length ? tagList.split('、').join('·') : ''
  68. return
  69. const { categoryId_dictText, timeId_dictText, ageId_dictText } = this.data
  70. return [categoryId_dictText, timeId_dictText, ageId_dictText].filter(val => val).join('·')
  71. },
  72. priceInt() {
  73. return Math.floor(this.data.priceDiscount)
  74. },
  75. priceFrac() {
  76. let frac = this.data.priceDiscount % this.priceInt
  77. return frac > 0 ? frac.toFixed(2).slice(1) : ''
  78. },
  79. isCollected() {
  80. return this.data.isCollection == '1'
  81. },
  82. collectBtnWidth() {
  83. return this.isCollected ? 80 : 56
  84. },
  85. collectBtnStyle() {
  86. const width = this.collectBtnWidth
  87. const background = this.isCollected ? '#26334E' : '#FF9035'
  88. let display = Math.ceil(this.displayX / width * 100)
  89. display > 100 && (display = 100)
  90. const translateX = 100 - display
  91. return `width: ${width}px; transform: translateX(${translateX}%); background: ${background};`
  92. },
  93. },
  94. methods: {
  95. onTouchstart(e) {
  96. const clientX = e.changedTouches[0].clientX
  97. this.isMove = false
  98. this.startClientX = clientX
  99. this.displayX = 0
  100. },
  101. onTouchmove(e) {
  102. const clientX = e.changedTouches[0].clientX
  103. if (clientX < this.startClientX) {
  104. this.displayX = this.startClientX - clientX
  105. } else {
  106. this.displayX = 0
  107. }
  108. this.isMove = true
  109. },
  110. onTouchend() {
  111. if (!this.isMove && !this.collectBtnVisible) {
  112. this.onRegistrate()
  113. }
  114. if (this.displayX < this.collectBtnWidth) {
  115. this.displayX = 0
  116. }
  117. if (this.displayX) {
  118. this.collectBtnVisible = true
  119. } else {
  120. this.collectBtnVisible = false
  121. }
  122. this.isMove = false
  123. },
  124. showCollectBtn() {
  125. this.displayX = 100
  126. },
  127. hiddenCollectBtn() {
  128. this.displayX = 0
  129. },
  130. async onCollect() {
  131. try {
  132. let succ = await this.$store.dispatch('collect', this.data.id)
  133. succ && this.hiddenCollectBtn()
  134. this.$emit('collect', !this.isCollected)
  135. } catch (err) {
  136. console.log('collect err', err)
  137. }
  138. },
  139. onRegistrate() {
  140. this.$utils.navigateTo(`/pages_order/product/productDetail?id=${this.data.id}`)
  141. },
  142. },
  143. }
  144. </script>
  145. <style scoped lang="scss">
  146. .product {
  147. position: relative;
  148. height: 464rpx;
  149. background: #FFFFFF;
  150. border: 2rpx solid #FFFFFF;
  151. border-radius: 32rpx;
  152. overflow: hidden;
  153. font-size: 0;
  154. &-img {
  155. width: 100%;
  156. height: 220rpx;
  157. }
  158. &-info {
  159. height: 244rpx;
  160. padding: 16rpx 16rpx 24rpx 16rpx;
  161. box-sizing: border-box;
  162. justify-content: space-between;
  163. &-top {
  164. width: 100%;
  165. }
  166. &-bottom {
  167. width: 100%;
  168. justify-content: space-between;
  169. }
  170. }
  171. &-name {
  172. font-size: 28rpx;
  173. font-weight: 500;
  174. color: #000000;
  175. }
  176. &-desc {
  177. margin-top: 8rpx;
  178. font-size: 24rpx;
  179. color: #8B8B8B;
  180. }
  181. &-detail {
  182. }
  183. &-price {
  184. justify-content: flex-start;
  185. align-items: baseline;
  186. column-gap: 12rpx;
  187. flex-wrap: wrap;
  188. &-val {
  189. font-size: 24rpx;
  190. font-weight: 500;
  191. color: #FF4800;
  192. white-space: nowrap;
  193. .highlight {
  194. font-size: 32rpx;
  195. }
  196. }
  197. &-bef {
  198. text-decoration: line-through;
  199. font-size: 24rpx;
  200. color: #8B8B8B;
  201. }
  202. }
  203. &-registered {
  204. font-size: 24rpx;
  205. color: #8B8B8B;
  206. }
  207. .btn {
  208. padding: 11rpx 32rpx;
  209. font-size: 26rpx;
  210. font-weight: 500;
  211. color: #FFFFFF;
  212. background: #00A9FF;
  213. border-radius: 24rpx;
  214. white-space: nowrap;
  215. }
  216. &.small {
  217. .btn {
  218. padding: 11rpx 16rpx;
  219. }
  220. }
  221. }
  222. .btn.btn-collect {
  223. position: absolute;
  224. top: 0;
  225. right: 0;
  226. row-gap: 8rpx;
  227. // width: 112rpx;
  228. height: 100%;
  229. font-size: 24rpx;
  230. line-height: 1;
  231. color: #FFFFFF;
  232. // background: #FF9035;
  233. border-radius: 0;
  234. }
  235. </style>