普兆健康管家前端代码仓库
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.

474 lines
9.9 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
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
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
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="page__view">
  3. <navbar bgColor="#F3F2F7" >
  4. <image class="nav-icon" :src="configList.icon_nav_dark" mode="widthFix"></image>
  5. </navbar>
  6. <view class="main">
  7. <view class="top">
  8. <view class="flex header">
  9. <view class="flex label">
  10. <view>购物车</view>
  11. <view v-if="total" class="desc">{{ `${total}` }}</view>
  12. </view>
  13. <button v-if="mode == 'edit'" class="btn" @click="onLeaveEdit">完成</button>
  14. <button v-else class="btn" @click="onEnterEdit">管理</button>
  15. </view>
  16. <!-- 搜索栏 -->
  17. <view class="search">
  18. <uv-search
  19. v-model="keyword"
  20. placeholder="请输入要查询的内容"
  21. placeholderColor="#C6C6C6"
  22. searchIconColor="#8B8B8B"
  23. :searchIconSize="40"
  24. :inputStyle="{
  25. 'font-family': 'PingFang SC',
  26. 'font-weight': 400,
  27. 'font-size': '28rpx',
  28. 'line-height': 1.4,
  29. 'padding': '12rpx 0',
  30. }"
  31. bgColor="#fff"
  32. :showAction="false"
  33. @search="search"
  34. ></uv-search>
  35. </view>
  36. </view>
  37. <template v-if="total > 0">
  38. <view>
  39. <view class="card" v-for="(item, index) in list" :key="item.id">
  40. <cart-product-card
  41. :data="item"
  42. @select="onSelect(index, $event)"
  43. @change="onChange(index, $event)"
  44. ></cart-product-card>
  45. </view>
  46. </view>
  47. </template>
  48. <template v-else-if="mode == 'edit'">
  49. <view class="flex flex-column empty">
  50. <view class="empty-title">购物车为空</view>
  51. </view>
  52. </template>
  53. <template v-else-if="queryParams.title">
  54. <view class="flex flex-column empty">
  55. <view class="empty-title">您的购物车里没有相关商品</view>
  56. </view>
  57. </template>
  58. <template v-else>
  59. <view class="flex flex-column empty">
  60. <view class="empty-title">购物车为空</view>
  61. <view class="empty-desc">请将喜欢的商品加入购物袋</view>
  62. </view>
  63. <view v-if="!queryParams.title" class="recommend">
  64. <view v-for="item in recommendList" :key="item.id">
  65. <recommend-product-card
  66. :data="item"
  67. cardStyle="width: 100%; height: 210px;"
  68. imgStyle="width: 100%; height: 110px;"
  69. @addCartSucc="getData"
  70. ></recommend-product-card>
  71. </view>
  72. </view>
  73. </template>
  74. </view>
  75. <view class="flex bottom">
  76. <template v-if="mode == 'edit'">
  77. <view class="flex bar bar-edit">
  78. <button class="btn" @click="onLeaveEdit">取消</button>
  79. <button :class="['btn', selectedList.length ? '' : 'is-disabled']" @click="onDelete" :disabled="!selectedList.length">删除</button>
  80. </view>
  81. </template>
  82. <template v-else>
  83. <view class="flex bar bar-settle">
  84. <view class="flex info">
  85. <image class="icon" src="@/pages_order/static/cart/cart-icon.png" mode="widthFix"></image>
  86. <view>
  87. <view class="flex row">已选<text class="count">{{ `${selectedList.length}` }}</text>已享受更低优惠</view>
  88. <view class="flex row">合计<text class="unit">¥</text><text class="price">{{ totalPrice }}</text></view>
  89. </view>
  90. </view>
  91. <button :class="['btn', selectedList.length ? '' : 'is-disabled']" :disabled="!selectedList.length" @click="onBuy">去结算</button>
  92. </view>
  93. </template>
  94. </view>
  95. <tabber select="cart" />
  96. </view>
  97. </template>
  98. <script>
  99. import { mapState } from 'vuex'
  100. import mixinsList from '@/mixins/list.js'
  101. import tabber from '@/components/base/tabbar.vue'
  102. import cartProductCard from '@/pages_order/cart/productCard.vue'
  103. import recommendProductCard from '@/pages_order/product/productCard.vue'
  104. export default {
  105. mixins: [mixinsList],
  106. components: {
  107. cartProductCard,
  108. recommendProductCard,
  109. tabber,
  110. },
  111. data() {
  112. return {
  113. mixinsListApi: 'getCartList',
  114. keyword: '',
  115. mode: 'read',
  116. selectedList: [],
  117. recommendList: [],
  118. }
  119. },
  120. computed: {
  121. ...mapState(['configList', 'userInfo']),
  122. // selectedList() {
  123. // return this.list.filter(item => item.selected)
  124. // },
  125. totalPrice() {
  126. return this.selectedList.reduce((price, item) => {
  127. return price + item.currentPrice
  128. }, 0)
  129. },
  130. },
  131. onLoad() {
  132. if(uni.getStorageSync('token')){
  133. this.$store.commit('getUserInfo')
  134. }
  135. },
  136. methods: {
  137. // 搜素
  138. search() {
  139. this.queryParams.pageNo = 1
  140. this.queryParams.pageSize = 10
  141. if (this.keyword) {
  142. this.queryParams.title = this.keyword
  143. } else {
  144. delete this.queryParams.title
  145. }
  146. this.getData()
  147. },
  148. async fetchRecommendProduct() {
  149. try {
  150. this.recommendList = await this.$fetch('getRecommendProduct')
  151. } catch (err) {
  152. }
  153. },
  154. getDataThen(records, total) {
  155. console.log('getDataThen', records, total)
  156. console.log('list', this.list, 'total', this.total)
  157. if (!total) {
  158. this.fetchRecommendProduct()
  159. this.updateSelectedList()
  160. return
  161. }
  162. this.selectedList.forEach(selectedItem => {
  163. let target = records.find(item => item.id === selectedItem.id)
  164. target.selected = true
  165. })
  166. this.list = records.map(item => {
  167. const { skuId, product, sku } = item
  168. const { currentPrice } = product || {}
  169. const { specName, price } = sku || {}
  170. return {
  171. ...item,
  172. currentPrice: skuId ? price : currentPrice,
  173. specId: skuId,
  174. specName: specName,
  175. }
  176. })
  177. this.recommendList = []
  178. this.updateSelectedList()
  179. },
  180. updateSelectedList() {
  181. this.selectedList = this.list.filter(item => item.selected)
  182. },
  183. onSelect(index, selected) {
  184. console.log('onSelect', index, selected)
  185. this.list[index].selected = selected
  186. this.updateSelectedList()
  187. },
  188. async onChange(index, obj) {
  189. console.log('onChange', index, obj)
  190. try {
  191. const target = this.list[index]
  192. const params = {
  193. id: target.id,
  194. skuId: obj.id
  195. }
  196. await this.$fetch('editCart', params)
  197. // this.getData()
  198. target.specId = obj.id
  199. target.specName = obj.specName
  200. target.currentPrice = obj.price
  201. this.updateSelectedList()
  202. } catch (err) {
  203. console.log('err', err)
  204. }
  205. },
  206. onEnterEdit() {
  207. this.mode = 'edit'
  208. },
  209. onLeaveEdit() {
  210. this.mode = 'read'
  211. },
  212. async fetchDelete() {
  213. try {
  214. const ids = this.selectedList.map(item => item.id)
  215. await this.$fetch('deleteCartBatch', { ids })
  216. this.getData()
  217. uni.showToast({
  218. icon: 'success',
  219. title: '删除成功',
  220. });
  221. } catch (err) {
  222. }
  223. },
  224. onDelete() {
  225. uni.showModal({
  226. title: '确认删除?',
  227. success : e => {
  228. if(e.confirm){
  229. this.fetchDelete()
  230. }
  231. }
  232. })
  233. },
  234. onBuy() {
  235. const list = this.selectedList.map(item => {
  236. const { id, product, currentPrice, specId, specName } = item
  237. return { ...product, currentPrice, specId, specName, cartId: id, }
  238. })
  239. this.$store.commit('createOrder', list)
  240. },
  241. },
  242. }
  243. </script>
  244. <style scoped lang="scss">
  245. .page__view {
  246. width: 100vw;
  247. min-height: 100vh;
  248. background-color: $uni-bg-color;
  249. position: relative;
  250. /deep/ .nav-bar__view {
  251. position: fixed;
  252. top: 0;
  253. left: 0;
  254. }
  255. .nav-icon {
  256. width: 200rpx;
  257. height: auto;
  258. vertical-align: top;
  259. }
  260. /deep/ .tabbar-box .tabbar {
  261. z-index: 9999;
  262. }
  263. }
  264. .main {
  265. padding: calc(var(--status-bar-height) + 288rpx) 32rpx 186rpx 32rpx;
  266. }
  267. .top {
  268. position: fixed;
  269. top: calc(var(--status-bar-height) + 120rpx);
  270. left: 0;
  271. width: 100%;
  272. height: 168rpx;
  273. padding: 16rpx 32rpx 8rpx 32rpx;
  274. background-color: $uni-bg-color;
  275. box-sizing: border-box;
  276. z-index: 1;
  277. }
  278. .header {
  279. justify-content: space-between;
  280. column-gap: 4rpx;
  281. .label {
  282. font-family: PingFang SC;
  283. font-weight: 600;
  284. font-size: 36rpx;
  285. line-height: 1.2;
  286. color: #252545;
  287. }
  288. .desc {
  289. font-family: PingFang SC;
  290. font-weight: 400;
  291. font-size: 24rpx;
  292. line-height: 1.4;
  293. color: #2A2A2A;
  294. }
  295. .btn {
  296. font-family: PingFang SC;
  297. font-weight: 600;
  298. font-size: 28rpx;
  299. line-height: 1.5;
  300. color: #FFFFFF;
  301. padding: 8rpx 32rpx;
  302. background-image: linear-gradient(to right, #4B348F, #845CFA);
  303. border-radius: 30rpx;
  304. }
  305. }
  306. .search {
  307. margin: 24rpx 0 40rpx 0;
  308. /deep/ .uv-search__content__icon {
  309. margin-top: 2rpx;
  310. }
  311. }
  312. .empty {
  313. width: 100%;
  314. font-family: PingFang SC;
  315. line-height: 1.4;
  316. padding: 104rpx 0;
  317. box-sizing: border-box;
  318. &-title {
  319. font-weight: 500;
  320. font-size: 32rpx;
  321. color: #000000;
  322. }
  323. &-desc {
  324. margin-top: 16rpx;
  325. font-weight: 400;
  326. font-size: 26rpx;
  327. color: #8B8B8B;
  328. }
  329. }
  330. .recommend {
  331. margin-top: 40rpx;
  332. display: grid;
  333. grid-template-columns: repeat(2, 1fr);
  334. gap: 32rpx;
  335. }
  336. .card {
  337. margin-top: 32rpx;
  338. }
  339. .bottom {
  340. position: fixed;
  341. left: 0;
  342. bottom: calc(env(safe-area-inset-bottom) + #{$tabbar-height});
  343. width: 100vw;
  344. height: 122rpx;
  345. padding: 0 40rpx;
  346. background: #FFFFFF;
  347. box-sizing: border-box;
  348. }
  349. .bar {
  350. width: 100%;
  351. &-edit {
  352. column-gap: 32rpx;
  353. .btn {
  354. flex: 1;
  355. padding: 16rpx 0;
  356. font-family: PingFang SC;
  357. font-weight: 500;
  358. font-size: 36rpx;
  359. line-height: 1;
  360. color: #252545;
  361. border: 2rpx solid #252545;
  362. border-radius: 41rpx;
  363. }
  364. }
  365. &-settle {
  366. justify-content: space-between;
  367. .info {
  368. column-gap: 16rpx;
  369. font-family: PingFang SC;
  370. font-weight: 400;
  371. font-size: 24rpx;
  372. line-height: 1.4;
  373. color: #626262;
  374. .icon {
  375. width: 76rpx;
  376. height: auto;
  377. }
  378. .row {
  379. justify-content: flex-start;
  380. }
  381. .count,
  382. .unit,
  383. .price {
  384. font-weight: 500;
  385. color: #7451DE;
  386. }
  387. .count {
  388. margin: 0 12rpx;
  389. }
  390. .unit {
  391. margin: 0 8rpx;
  392. }
  393. .price {
  394. font-size: 40rpx;
  395. }
  396. }
  397. .btn {
  398. padding: 16rpx 46rpx;
  399. color: #FFFFFF;
  400. font-family: PingFang SC;
  401. font-weight: 500;
  402. font-size: 36rpx;
  403. line-height: 1;
  404. background-image: linear-gradient(to right, #4B348F, #845CFA);
  405. border-radius: 41rpx;
  406. }
  407. }
  408. }
  409. </style>