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

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