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

513 lines
11 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month 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" @click="onDelete">删除</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. // todo
  103. mixinsListApi: '',
  104. // todo
  105. keyword: '',
  106. mode: 'read',
  107. selectedList: [],
  108. recommendList: [],
  109. }
  110. },
  111. computed: {
  112. ...mapState(['configList', 'userInfo']),
  113. // selectedList() {
  114. // return this.list.filter(item => item.selected)
  115. // },
  116. totalPrice() {
  117. return this.selectedList.reduce((price, item) => {
  118. // return price + item.price * (item.count || 1)
  119. return price + item.price
  120. }, 0)
  121. },
  122. },
  123. methods: {
  124. // 搜素
  125. search() {
  126. // todo
  127. },
  128. // todo: delete
  129. getData() {
  130. this.list = [
  131. {
  132. id: '001',
  133. url: '',
  134. name: '月度装定制营养包',
  135. price: 688,
  136. count: 1,
  137. countDesc: '1月装',
  138. options: [
  139. { id: '001', label: '1月装', count: 1, value: 688 },
  140. { id: '002', label: '3月装', count: 3, value: 688*3 },
  141. { id: '003', label: '6月装', count: 6, value: 688*6 },
  142. ],
  143. customized: true,
  144. checked: false,
  145. },
  146. {
  147. id: '002',
  148. url: '',
  149. name: '月度装定制营养包',
  150. price: 688,
  151. count: 1,
  152. countDesc: '1月装',
  153. options: [
  154. { id: '001', label: '1月装', count: 1, value: 688 },
  155. { id: '002', label: '3月装', count: 3, value: 688*3 },
  156. { id: '003', label: '6月装', count: 6, value: 688*6 },
  157. ],
  158. free: true,
  159. checked: false,
  160. },
  161. {
  162. id: '003',
  163. url: '',
  164. name: '月度装定制营养包',
  165. price: 688,
  166. count: 1,
  167. countDesc: '1月装',
  168. options: [
  169. { id: '001', label: '1月装', count: 1, value: 688 },
  170. { id: '002', label: '3月装', count: 3, value: 688*3 },
  171. { id: '003', label: '6月装', count: 6, value: 688*6 },
  172. ],
  173. free: true,
  174. checked: false,
  175. },
  176. {
  177. id: '004',
  178. url: '',
  179. name: '月度装定制营养包',
  180. price: 688,
  181. count: 1,
  182. countDesc: '1月装',
  183. options: [
  184. { id: '001', label: '1月装', count: 1, value: 688 },
  185. { id: '002', label: '3月装', count: 3, value: 688*3 },
  186. { id: '003', label: '6月装', count: 6, value: 688*6 },
  187. ],
  188. free: true,
  189. checked: false,
  190. },
  191. {
  192. id: '005',
  193. url: '',
  194. name: '月度装定制营养包',
  195. price: 688,
  196. count: 1,
  197. countDesc: '1月装',
  198. options: [
  199. { id: '001', label: '1月装', count: 1, value: 688 },
  200. { id: '002', label: '3月装', count: 3, value: 688*3 },
  201. { id: '003', label: '6月装', count: 6, value: 688*6 },
  202. ],
  203. free: true,
  204. checked: false,
  205. },
  206. ]
  207. this.total = this.list.length
  208. this.getDataThen && this.getDataThen(this.list, this.total)
  209. },
  210. getDataThen(records, total) {
  211. console.log('getDataThen')
  212. // todo
  213. if (total) {
  214. return
  215. }
  216. this.recommendList = [
  217. {
  218. id: '001',
  219. url: '',
  220. name: '月度装定制营养包',
  221. sales: 24770,
  222. price: 688.00,
  223. originalPrice: 1664,
  224. },
  225. {
  226. id: '002',
  227. url: '',
  228. name: '月度装定制营养包',
  229. sales: 24770,
  230. price: 688.00,
  231. originalPrice: 1664,
  232. },
  233. {
  234. id: '003',
  235. url: '',
  236. name: '月度装定制营养包',
  237. sales: 24770,
  238. price: 688.00,
  239. originalPrice: 1664,
  240. },
  241. {
  242. id: '004',
  243. url: '',
  244. name: '月度装定制营养包',
  245. sales: 24770,
  246. price: 688.00,
  247. originalPrice: 1664,
  248. },
  249. ]
  250. },
  251. updateSelectedList() {
  252. this.selectedList = this.list.filter(item => item.selected)
  253. },
  254. onSelect(index, selected) {
  255. console.log('onSelect', index, selected)
  256. this.list[index].selected = selected
  257. // todo
  258. this.updateSelectedList()
  259. },
  260. onChange(index, obj) {
  261. console.log('onChange', index, obj)
  262. // todo: check
  263. this.list[index].price = obj.price
  264. this.list[index].count = obj.count
  265. this.list[index].countDesc = obj.countDesc
  266. // todo
  267. this.updateSelectedList()
  268. },
  269. onEnterEdit() {
  270. this.mode = 'edit'
  271. },
  272. onLeaveEdit() {
  273. this.mode = 'read'
  274. },
  275. onDelete() {
  276. uni.showModal({
  277. title: '确认删除?',
  278. success : e => {
  279. if(e.confirm){
  280. // todo
  281. this.list = this.list.filter(item => !item.selected)
  282. this.total = this.list.length
  283. }
  284. }
  285. })
  286. },
  287. onBuy() {
  288. this.$store.commit('createOrder', this.selectedList)
  289. },
  290. },
  291. }
  292. </script>
  293. <style scoped lang="scss">
  294. .page__view {
  295. width: 100vw;
  296. min-height: 100vh;
  297. background-color: $uni-bg-color;
  298. position: relative;
  299. /deep/ .nav-bar__view {
  300. position: fixed;
  301. top: 0;
  302. left: 0;
  303. }
  304. .nav-icon {
  305. width: 200rpx;
  306. height: auto;
  307. vertical-align: top;
  308. }
  309. /deep/ .tabbar-box .tabbar {
  310. z-index: 9999;
  311. }
  312. }
  313. .main {
  314. padding: calc(var(--status-bar-height) + 288rpx) 32rpx 186rpx 32rpx;
  315. }
  316. .top {
  317. position: fixed;
  318. top: calc(var(--status-bar-height) + 120rpx);
  319. left: 0;
  320. width: 100%;
  321. height: 168rpx;
  322. padding: 16rpx 32rpx 8rpx 32rpx;
  323. background-color: $uni-bg-color;
  324. box-sizing: border-box;
  325. z-index: 1;
  326. }
  327. .header {
  328. justify-content: space-between;
  329. column-gap: 4rpx;
  330. .label {
  331. font-family: PingFang SC;
  332. font-weight: 600;
  333. font-size: 36rpx;
  334. line-height: 1.2;
  335. color: #252545;
  336. }
  337. .desc {
  338. font-family: PingFang SC;
  339. font-weight: 400;
  340. font-size: 24rpx;
  341. line-height: 1.4;
  342. color: #2A2A2A;
  343. }
  344. .btn {
  345. font-family: PingFang SC;
  346. font-weight: 600;
  347. font-size: 28rpx;
  348. line-height: 1.5;
  349. color: #FFFFFF;
  350. padding: 8rpx 32rpx;
  351. background-image: linear-gradient(to right, #4B348F, #845CFA);
  352. border-radius: 30rpx;
  353. }
  354. }
  355. .search {
  356. margin: 24rpx 0 40rpx 0;
  357. /deep/ .uv-search__content__icon {
  358. margin-top: 2rpx;
  359. }
  360. }
  361. .empty {
  362. width: 100%;
  363. font-family: PingFang SC;
  364. line-height: 1.4;
  365. padding: 104rpx 0;
  366. box-sizing: border-box;
  367. &-title {
  368. font-weight: 500;
  369. font-size: 32rpx;
  370. color: #000000;
  371. }
  372. &-desc {
  373. margin-top: 16rpx;
  374. font-weight: 400;
  375. font-size: 26rpx;
  376. color: #8B8B8B;
  377. }
  378. }
  379. .recommend {
  380. margin-top: 40rpx;
  381. display: grid;
  382. grid-template-columns: repeat(2, 1fr);
  383. gap: 32rpx;
  384. }
  385. .card {
  386. margin-top: 32rpx;
  387. }
  388. .bottom {
  389. position: fixed;
  390. left: 0;
  391. bottom: calc(env(safe-area-inset-bottom) + #{$tabbar-height});
  392. width: 100vw;
  393. height: 122rpx;
  394. padding: 0 40rpx;
  395. background: #FFFFFF;
  396. box-sizing: border-box;
  397. }
  398. .bar {
  399. width: 100%;
  400. &-edit {
  401. column-gap: 32rpx;
  402. .btn {
  403. flex: 1;
  404. padding: 16rpx 0;
  405. font-family: PingFang SC;
  406. font-weight: 500;
  407. font-size: 36rpx;
  408. line-height: 1;
  409. color: #252545;
  410. border: 2rpx solid #252545;
  411. border-radius: 41rpx;
  412. }
  413. }
  414. &-settle {
  415. justify-content: space-between;
  416. .info {
  417. column-gap: 16rpx;
  418. font-family: PingFang SC;
  419. font-weight: 400;
  420. font-size: 24rpx;
  421. line-height: 1.4;
  422. color: #626262;
  423. .icon {
  424. width: 76rpx;
  425. height: auto;
  426. }
  427. .row {
  428. justify-content: flex-start;
  429. }
  430. .count,
  431. .unit,
  432. .price {
  433. font-weight: 500;
  434. color: #7451DE;
  435. }
  436. .count {
  437. margin: 0 12rpx;
  438. }
  439. .unit {
  440. margin: 0 8rpx;
  441. }
  442. .price {
  443. font-size: 40rpx;
  444. }
  445. }
  446. .btn {
  447. padding: 16rpx 46rpx;
  448. color: #FFFFFF;
  449. font-family: PingFang SC;
  450. font-weight: 500;
  451. font-size: 36rpx;
  452. line-height: 1;
  453. background-image: linear-gradient(to right, #4B348F, #845CFA);
  454. border-radius: 41rpx;
  455. &.is-disabled {
  456. opacity: 0.5;
  457. }
  458. }
  459. }
  460. }
  461. </style>