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

478 lines
10 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
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. // todo: check key
  128. // return price + item.price * (item.count || 1)
  129. return price + item.currentPrice
  130. }, 0)
  131. },
  132. },
  133. onLoad() {
  134. if(uni.getStorageSync('token')){
  135. this.$store.commit('getUserInfo')
  136. }
  137. },
  138. methods: {
  139. // 搜素
  140. search() {
  141. this.queryParams.pageNo = 1
  142. this.queryParams.pageSize = 10
  143. if (this.keyword) {
  144. this.queryParams.title = this.keyword
  145. } else {
  146. delete this.queryParams.title
  147. }
  148. this.getData()
  149. },
  150. async fetchRecommendProduct() {
  151. try {
  152. this.recommendList = await this.$fetch('getRecommendProduct')
  153. } catch (err) {
  154. }
  155. },
  156. getDataThen(records, total) {
  157. console.log('getDataThen', records, total)
  158. console.log('list', this.list, 'total', this.total)
  159. if (!total) {
  160. this.fetchRecommendProduct()
  161. this.updateSelectedList()
  162. return
  163. }
  164. this.selectedList.forEach(selectedItem => {
  165. let target = records.find(item => item.id === selectedItem.id)
  166. target.selected = true
  167. })
  168. this.list = records.map(item => {
  169. const { skuId, product, sku } = item
  170. const { currentPrice } = product || {}
  171. const { specName, price } = sku || {}
  172. return {
  173. ...item,
  174. currentPrice: skuId ? price : currentPrice,
  175. specId: skuId,
  176. specName: specName,
  177. }
  178. })
  179. this.recommendList = []
  180. this.updateSelectedList()
  181. },
  182. updateSelectedList() {
  183. this.selectedList = this.list.filter(item => item.selected)
  184. },
  185. onSelect(index, selected) {
  186. console.log('onSelect', index, selected)
  187. this.list[index].selected = selected
  188. this.updateSelectedList()
  189. },
  190. async onChange(index, obj) {
  191. console.log('onChange', index, obj)
  192. try {
  193. const target = this.list[index]
  194. const params = {
  195. id: target.id,
  196. skuId: obj.id
  197. }
  198. await this.$fetch('editCart', params)
  199. // this.getData()
  200. target.specId = obj.id
  201. target.specName = obj.specName
  202. target.currentPrice = obj.price
  203. this.updateSelectedList()
  204. } catch (err) {
  205. console.log('err', err)
  206. }
  207. },
  208. onEnterEdit() {
  209. this.mode = 'edit'
  210. },
  211. onLeaveEdit() {
  212. this.mode = 'read'
  213. },
  214. async fetchDelete() {
  215. try {
  216. const ids = this.selectedList.map(item => item.id)
  217. await this.$fetch('deleteCartBatch', { ids })
  218. this.getData()
  219. uni.showToast({
  220. icon: 'success',
  221. title: '删除成功',
  222. });
  223. } catch (err) {
  224. }
  225. },
  226. onDelete() {
  227. uni.showModal({
  228. title: '确认删除?',
  229. success : e => {
  230. if(e.confirm){
  231. this.fetchDelete()
  232. }
  233. }
  234. })
  235. },
  236. onBuy() {
  237. // todo: check id
  238. const list = this.selectedList.map(item => {
  239. const { id, product, currentPrice, specId, specName } = item
  240. return { ...product, currentPrice, specId, specName, cartId: id, }
  241. })
  242. this.$store.commit('createOrder', list)
  243. },
  244. },
  245. }
  246. </script>
  247. <style scoped lang="scss">
  248. .page__view {
  249. width: 100vw;
  250. min-height: 100vh;
  251. background-color: $uni-bg-color;
  252. position: relative;
  253. /deep/ .nav-bar__view {
  254. position: fixed;
  255. top: 0;
  256. left: 0;
  257. }
  258. .nav-icon {
  259. width: 200rpx;
  260. height: auto;
  261. vertical-align: top;
  262. }
  263. /deep/ .tabbar-box .tabbar {
  264. z-index: 9999;
  265. }
  266. }
  267. .main {
  268. padding: calc(var(--status-bar-height) + 288rpx) 32rpx 186rpx 32rpx;
  269. }
  270. .top {
  271. position: fixed;
  272. top: calc(var(--status-bar-height) + 120rpx);
  273. left: 0;
  274. width: 100%;
  275. height: 168rpx;
  276. padding: 16rpx 32rpx 8rpx 32rpx;
  277. background-color: $uni-bg-color;
  278. box-sizing: border-box;
  279. z-index: 1;
  280. }
  281. .header {
  282. justify-content: space-between;
  283. column-gap: 4rpx;
  284. .label {
  285. font-family: PingFang SC;
  286. font-weight: 600;
  287. font-size: 36rpx;
  288. line-height: 1.2;
  289. color: #252545;
  290. }
  291. .desc {
  292. font-family: PingFang SC;
  293. font-weight: 400;
  294. font-size: 24rpx;
  295. line-height: 1.4;
  296. color: #2A2A2A;
  297. }
  298. .btn {
  299. font-family: PingFang SC;
  300. font-weight: 600;
  301. font-size: 28rpx;
  302. line-height: 1.5;
  303. color: #FFFFFF;
  304. padding: 8rpx 32rpx;
  305. background-image: linear-gradient(to right, #4B348F, #845CFA);
  306. border-radius: 30rpx;
  307. }
  308. }
  309. .search {
  310. margin: 24rpx 0 40rpx 0;
  311. /deep/ .uv-search__content__icon {
  312. margin-top: 2rpx;
  313. }
  314. }
  315. .empty {
  316. width: 100%;
  317. font-family: PingFang SC;
  318. line-height: 1.4;
  319. padding: 104rpx 0;
  320. box-sizing: border-box;
  321. &-title {
  322. font-weight: 500;
  323. font-size: 32rpx;
  324. color: #000000;
  325. }
  326. &-desc {
  327. margin-top: 16rpx;
  328. font-weight: 400;
  329. font-size: 26rpx;
  330. color: #8B8B8B;
  331. }
  332. }
  333. .recommend {
  334. margin-top: 40rpx;
  335. display: grid;
  336. grid-template-columns: repeat(2, 1fr);
  337. gap: 32rpx;
  338. }
  339. .card {
  340. margin-top: 32rpx;
  341. }
  342. .bottom {
  343. position: fixed;
  344. left: 0;
  345. bottom: calc(env(safe-area-inset-bottom) + #{$tabbar-height});
  346. width: 100vw;
  347. height: 122rpx;
  348. padding: 0 40rpx;
  349. background: #FFFFFF;
  350. box-sizing: border-box;
  351. }
  352. .bar {
  353. width: 100%;
  354. &-edit {
  355. column-gap: 32rpx;
  356. .btn {
  357. flex: 1;
  358. padding: 16rpx 0;
  359. font-family: PingFang SC;
  360. font-weight: 500;
  361. font-size: 36rpx;
  362. line-height: 1;
  363. color: #252545;
  364. border: 2rpx solid #252545;
  365. border-radius: 41rpx;
  366. }
  367. }
  368. &-settle {
  369. justify-content: space-between;
  370. .info {
  371. column-gap: 16rpx;
  372. font-family: PingFang SC;
  373. font-weight: 400;
  374. font-size: 24rpx;
  375. line-height: 1.4;
  376. color: #626262;
  377. .icon {
  378. width: 76rpx;
  379. height: auto;
  380. }
  381. .row {
  382. justify-content: flex-start;
  383. }
  384. .count,
  385. .unit,
  386. .price {
  387. font-weight: 500;
  388. color: #7451DE;
  389. }
  390. .count {
  391. margin: 0 12rpx;
  392. }
  393. .unit {
  394. margin: 0 8rpx;
  395. }
  396. .price {
  397. font-size: 40rpx;
  398. }
  399. }
  400. .btn {
  401. padding: 16rpx 46rpx;
  402. color: #FFFFFF;
  403. font-family: PingFang SC;
  404. font-weight: 500;
  405. font-size: 36rpx;
  406. line-height: 1;
  407. background-image: linear-gradient(to right, #4B348F, #845CFA);
  408. border-radius: 41rpx;
  409. }
  410. }
  411. }
  412. </style>