shoppingCart.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <template>
  2. <view class="page">
  3. <cu-custom :isBack="true"></cu-custom>
  4. <view class="header" :style="[{ top: topHeader + 'px' }]">
  5. <view class="header-title">
  6. <view class="header-title-main">
  7. <view class="margin-bottom-xs">{{ title }}</view>
  8. <view class="point"></view>
  9. </view>
  10. </view>
  11. </view>
  12. <scroll-view scroll-y="true" :style="[{ height: 'calc(100vh - 64px - 70px - ' + topHeader + 'px)' }]" class="scroll-main">
  13. <view class="list">
  14. <checkbox-group @change="checkChange">
  15. <view class="cu-card margin-bottom shadow" v-for="(item, index) in list" :key="index">
  16. <label class="cu-item">
  17. <view class="card-header flex justify-between solid-bottom">
  18. <view>
  19. <checkbox style="transform: scale(0.8);" class="round" :value="item.shopping_id" :checked="checkedIds.includes(item.shopping_id)"></checkbox>
  20. {{ item.student_name }}
  21. </view>
  22. <view @tap.stop="">
  23. <text class="cuIcon-delete text-red" @tap="del(item, index)"></text>
  24. </view>
  25. </view>
  26. <view class="card flex">
  27. <view class="card-left">
  28. <img mode="scaleToFill" :src="item.image" alt="" class="card-image" />
  29. </view>
  30. <view class="card-right flex-sub margin-left-sm text-sm">
  31. <view class="card-title">{{ item.class_attend_name }}</view>
  32. <view class="text-price text-student text-xl margin-top-xs">{{ item.price }}</view>
  33. <view class="card-item">
  34. <text class="card-label">机构:</text>
  35. <text class="card-text">{{ item.agency_name || '-' }}</text>
  36. </view>
  37. <view class="card-item margin-top-xs" v-if="item.cate_type === 0">
  38. <text class="card-label">老师:</text>
  39. <text class="card-text">{{ item.teacher_name || '-' }}</text>
  40. </view>
  41. <view class="card-item margin-top-xs">
  42. <text class="card-label">总共:</text>
  43. <text class="card-text">{{ item.class_total || '-' }}</text>
  44. </view>
  45. <view class="card-item margin-top-xs">
  46. <text class="card-label">时间:</text>
  47. <text class="card-text">{{ item.week_limit || '-' }}</text>
  48. </view>
  49. </view>
  50. </view>
  51. <view class="card-footer margin-top-xs" v-if="item.prop">
  52. <view v-for="(tag, index) in item.prop" :key="index" class="cu-tag line-red">{{ tag }}</view>
  53. </view>
  54. <view class="card-bottom margin-top-xs">
  55. <!-- <button @tap="pay(item)" class="cu-btn round bg-student text-white">立即下单</button> -->
  56. </view>
  57. </label>
  58. </view>
  59. </checkbox-group>
  60. </view>
  61. </scroll-view>
  62. <view class="static flex shadow">
  63. <view class="static-price">
  64. <checkbox-group @change="chooseAll">
  65. <label class="padding-tb-sm"> <checkbox class="round" :checked="checkAll" style="transform: scale(0.8);" />全选 </label>
  66. </checkbox-group>
  67. </view>
  68. <view class="static-choose text-center" v-if="checkedIds.length > 0">
  69. <text>已选:</text><text class="margin-right-xs">{{ checkedIds.length }}</text> <text>总价:</text><text class="text-price text-red">{{ sum }}</text>
  70. </view>
  71. <view class="static-order flex align-center">
  72. <button class="cu-btn round bg-student text-white margin-left-xs" @tap="pay">立即下单</button>
  73. </view>
  74. </view>
  75. </view>
  76. </template>
  77. <script>
  78. import { _shopList, _delShop, _createOrder } from '@/api/course'
  79. import { mapGetters } from 'vuex'
  80. import { deepClone } from '@/common/utils'
  81. import NP from 'number-precision'
  82. import socket from '@/common/webSocket'
  83. export default {
  84. data() {
  85. return {
  86. title: '购物车',
  87. topHeader: this.globalCustomBarHeight,
  88. list: [],
  89. checkedIds: [], // 已选择
  90. shoppingList: [], // 购物车列表
  91. sum: 0,
  92. all: [], // 全部
  93. payable: false, //控制重复点击
  94. checkAll: false
  95. }
  96. },
  97. computed: {
  98. ...mapGetters(['carts', 'phone', 'ifConnectOrder'])
  99. },
  100. onShow() {
  101. this.get_list()
  102. },
  103. onLoad(options) {
  104. socket.initSocket()
  105. },
  106. methods: {
  107. subscribeMsg() {
  108. return new Promise((resolve, reject) => {
  109. uni.requestSubscribeMessage({
  110. tmplIds: ['tfuaGZ8IOk0-JDKMcr5bVhUiiqvIORJHR_74VoPuWH4'],
  111. success(res) {},
  112. fail(res) {},
  113. complete(res) {
  114. resolve(true)
  115. }
  116. })
  117. })
  118. },
  119. checkChange(e) {
  120. this.checkedIds = e.detail.value.map(x => Number(x))
  121. this.shoppingList = this.list.filter(item => this.checkedIds.includes(item.shopping_id))
  122. this.sum_money()
  123. },
  124. chooseAll() {
  125. this.checkAll = !this.checkAll
  126. this.checkedIds = this.checkAll ? this.all : []
  127. this.shoppingList = this.checkAll ? deepClone(this.list) : []
  128. this.sum_money()
  129. },
  130. sum_money() {
  131. const arr = this.shoppingList.map(item => item.price)
  132. this.sum = arr.reduce((x, y) => NP.plus(x, y), 0)
  133. },
  134. get_list() {
  135. _shopList().then(res => {
  136. this.list = res.data
  137. this.all = this.list.map(item => item.shopping_id) // 统计全部
  138. const carts = this.list.map(item => item.class_attend_id)
  139. this.$store.dispatch('setCarts', carts) // 刷新购物车
  140. })
  141. },
  142. del(item, index) {
  143. const _self = this
  144. uni.showModal({
  145. title: '提示',
  146. content: '确定要删除?',
  147. cancelText: '取消',
  148. confirmText: '确定',
  149. success: res => {
  150. if (res.confirm) {
  151. _delShop({ shopping_id: item.shopping_id, student_id: item.student_id }).then(res => {
  152. _self.get_list()
  153. })
  154. }
  155. }
  156. })
  157. },
  158. pay() {
  159. if (this.payable) return false
  160. this.payable = true
  161. if (!this.phone) {
  162. return this.globalNavigateTo('bindPhone')
  163. }
  164. if (this.checkedIds.length < 1) {
  165. this.payable = false
  166. uni.showToast({ title: '请先选择课程', icon: 'none', duration: 1000 })
  167. return false
  168. }
  169. //订阅消息
  170. this.subscribeMsg().then(res => {
  171. this.placeOrder() //下单
  172. })
  173. },
  174. placeOrder() {
  175. uni.showLoading({
  176. mask: true,
  177. title: '加载中...'
  178. })
  179. const order = JSON.stringify(this.shoppingList)
  180. let carts = deepClone(this.carts)
  181. const shoppingCarts = this.shoppingList.map(item => item.class_attend_id)
  182. carts = carts.filter(item => !shoppingCarts.includes(item)) //购物车存储
  183. _createOrder({ result: order, notShowLoading: true }).then(res => {
  184. this.$store.dispatch('setCarts', carts)
  185. setTimeout(() => {
  186. if (!this.ifConnectOrder) {
  187. // socket非正常状态
  188. uni.hideLoading()
  189. this.globalNavigateTo('order', { type: 1 })
  190. }
  191. this.payable = false
  192. this.$store.dispatch('setConnect', false)
  193. }, 3000)
  194. })
  195. }
  196. }
  197. }
  198. </script>
  199. <style lang="scss" scoped>
  200. @import '~@/common/css/mixin.scss';
  201. .scroll-main {
  202. margin-top: 64px;
  203. padding: 20rpx;
  204. }
  205. .cu-card {
  206. .cu-item {
  207. margin-top: 10rpx;
  208. padding: 20rpx;
  209. margin: 0;
  210. }
  211. }
  212. .card-header {
  213. padding-bottom: 10px;
  214. @include title(10px, 18px);
  215. }
  216. .card {
  217. &-title {
  218. font-size: 16px;
  219. }
  220. &-image {
  221. width: 35vw;
  222. height: 35vw;
  223. border-radius: 10rpx;
  224. }
  225. &-label {
  226. color: #999;
  227. }
  228. &-text {
  229. color: #333;
  230. }
  231. }
  232. .card-bottom {
  233. text-align: right;
  234. }
  235. .static {
  236. position: fixed;
  237. left: 0;
  238. right: 0;
  239. bottom: 0;
  240. display: flex;
  241. background: #fff;
  242. height: 70px;
  243. align-items: center;
  244. padding: 0 0.8rem;
  245. justify-content: space-between;
  246. &-price {
  247. font-size: 14px;
  248. }
  249. &-choose {
  250. color: #666;
  251. width: calc(100vw - 200px);
  252. }
  253. }
  254. </style>