鸿宇研学生前端代码
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.

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