commodity.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. <template>
  2. <view class="container">
  3. <!-- 一级分类 -->
  4. <scroll-view class="cate-left" :scroll-y="true" :style="{ height: `${scrollHeight}px` }">
  5. <text class="type-nav" :class="{ selected: curIndex == -1 }" @click="handleSelectNav(-1)">全部</text>
  6. <text class="type-nav" :class="{ selected: curIndex == index }" v-for="(item, index) in list" :key="index"
  7. @click="handleSelectNav(index)">{{ item.name }}</text>
  8. </scroll-view>
  9. <mescroll-body ref="mescrollRef" :sticky="true" @init="mescrollInit" :down="{ use: false }" :up="upOption" :bottombar="false" @up="upCallback">
  10. <view class="cate-content">
  11. <!-- 商品列表 -->
  12. <view class="goods-list">
  13. <view class="goods-item--container" v-for="(item, index) in goodsList.data" :key="index">
  14. <view class="goods-item" @click="onTargetGoods(item.goods_id)">
  15. <!-- 商品图片 -->
  16. <view class="goods-item_left">
  17. <image class="image" :src="item.goods_image"></image>
  18. </view>
  19. <view class="goods-item_right">
  20. <!-- 商品标题 -->
  21. <view class="goods-name">
  22. <text class="twoline-hide">{{ item.goods_name }}</text>
  23. </view>
  24. <!-- 商品信息 -->
  25. <view class="goods-item_desc">
  26. <view class="desc_footer">
  27. <view class="item-prices oneline-hide">
  28. <text class="price_x">¥{{ item.goods_price_min }}</text>
  29. <text v-if="item.line_price_min > 0" class="price_y">¥{{ item.line_price_min }}</text>
  30. </view>
  31. <add-cart-btn v-if="setting.showAddCart" :btnStyle="setting.cartStyle" @click="handleAddCart(item)" />
  32. </view>
  33. </view>
  34. </view>
  35. </view>
  36. </view>
  37. </view>
  38. <!-- 加入购物车组件 -->
  39. <AddCartPopup ref="AddCartPopup" @addCart="onUpdateCartTabBadge" />
  40. </view>
  41. </mescroll-body>
  42. </view>
  43. </template>
  44. <script>
  45. import MescrollBody from '@/components/mescroll-uni/mescroll-body.vue'
  46. import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins'
  47. import { getEmptyPaginateObj, getMoreListData, setCartTabBadge } from '@/core/app'
  48. import { PageCategoryStyleEnum } from '@/common/enum/store/page/category'
  49. import Empty from '@/components/empty'
  50. import AddCartBtn from '@/components/add-cart-btn'
  51. import AddCartPopup from '@/components/add-cart-popup'
  52. import { rpx2px } from '@/utils/util'
  53. import * as GoodsApi from '@/api/goods'
  54. const pageSize = 15
  55. export default {
  56. components: {
  57. MescrollBody,
  58. Empty,
  59. AddCartBtn,
  60. AddCartPopup
  61. },
  62. mixins: [MescrollMixin],
  63. props: {
  64. // 分类列表
  65. list: {
  66. type: Array,
  67. default: []
  68. },
  69. // 分类设置
  70. setting: {
  71. type: Object,
  72. default: () => {}
  73. },
  74. },
  75. data() {
  76. return {
  77. // 枚举类
  78. PageCategoryStyleEnum,
  79. // 列表高度
  80. scrollHeight: 0,
  81. // 一级分类:指针
  82. curIndex: -1,
  83. // 商品列表
  84. goodsList: getEmptyPaginateObj(),
  85. // 上拉加载配置
  86. upOption: {
  87. // 首次自动执行
  88. auto: true,
  89. // 每页数据的数量; 默认10
  90. page: { size: pageSize },
  91. // 数量要大于3条才显示无更多数据
  92. noMoreSize: 3,
  93. // 返回顶部
  94. toTop: { right: 30, bottom: 48, zIndex: 9 }
  95. }
  96. }
  97. },
  98. created() {
  99. // 设置分类列表高度
  100. this.setListHeight()
  101. },
  102. methods: {
  103. /**
  104. * 上拉加载的回调 (页面初始化时也会执行一次)
  105. * 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10
  106. * @param {Object} page
  107. */
  108. upCallback(page) {
  109. const app = this
  110. // 设置列表数据
  111. app.getGoodsList(page.num)
  112. .then(list => {
  113. const curPageLen = list.data.length
  114. const totalSize = list.data.total
  115. app.mescroll.endBySize(curPageLen, totalSize)
  116. })
  117. .catch(() => app.mescroll.endErr())
  118. },
  119. /**
  120. * 获取商品列表
  121. * @param {Number} pageNo 页码
  122. */
  123. getGoodsList(pageNo = 1) {
  124. const app = this
  125. const categoryId = app.curIndex > -1 ? app.list[app.curIndex].category_id : 0
  126. return new Promise((resolve, reject) => {
  127. GoodsApi.list({ categoryId, page: pageNo }, { load: false })
  128. .then(result => {
  129. const newList = result.data.list
  130. app.goodsList.data = getMoreListData(newList, app.goodsList, pageNo)
  131. app.goodsList.last_page = newList.last_page
  132. resolve(newList)
  133. })
  134. .catch(reject)
  135. })
  136. },
  137. // 设置列表内容的高度
  138. setListHeight() {
  139. const { windowHeight } = uni.getSystemInfoSync()
  140. this.scrollHeight = windowHeight - rpx2px(96)
  141. },
  142. // 一级分类:选中分类
  143. handleSelectNav(index) {
  144. this.curIndex = index
  145. this.onRefreshList()
  146. },
  147. // 刷新列表数据
  148. onRefreshList() {
  149. this.goodsList = getEmptyPaginateObj()
  150. setTimeout(() => this.mescroll.resetUpScroll(), 120)
  151. },
  152. // 跳转至商品列表页
  153. onTargetGoods(goodsId) {
  154. this.$navTo('pages/goods/detail', { goodsId })
  155. },
  156. // 点击加入购物车
  157. handleAddCart(item) {
  158. this.$refs.AddCartPopup.handle(item)
  159. },
  160. // 更新购物车角标
  161. onUpdateCartTabBadge() {
  162. console.log('onUpdateCartTabBadge')
  163. setCartTabBadge()
  164. },
  165. }
  166. }
  167. </script>
  168. <style lang="scss" scoped>
  169. .container {
  170. padding-left: 173rpx;
  171. }
  172. // 分类内容
  173. .cate-content {
  174. z-index: 1;
  175. background: #fff;
  176. padding-top: 96rpx;
  177. }
  178. // 一级分类+二级分类 20
  179. .cate-left {
  180. width: 173rpx;
  181. height: 100%;
  182. background: #f8f8f8;
  183. color: #444;
  184. position: fixed;
  185. top: calc(96rpx + var(--window-top));
  186. left: var(--window-left);
  187. bottom: var(--window-bottom);
  188. }
  189. // 左侧一级分类
  190. .type-nav {
  191. position: relative;
  192. height: 90rpx;
  193. z-index: 10;
  194. display: block;
  195. font-size: 26rpx;
  196. display: flex;
  197. justify-content: center;
  198. align-items: center;
  199. &.selected {
  200. background: #fff;
  201. color: #fa2209;
  202. border-right: none;
  203. font-size: 28rpx;
  204. }
  205. }
  206. // 商品列表
  207. .goods-list {
  208. background: #fff;
  209. }
  210. .goods-item {
  211. padding: 28rpx 22rpx;
  212. display: flex;
  213. }
  214. .goods-item_left {
  215. position: relative;
  216. background: #fff;
  217. margin-right: 20rpx;
  218. .image {
  219. display: block;
  220. width: 180rpx;
  221. height: 180rpx;
  222. }
  223. }
  224. .goods-item_right {
  225. position: relative;
  226. flex: 1;
  227. .goods-name {
  228. display: block;
  229. width: 100%;
  230. min-height: 68rpx;
  231. font-size: 28rpx;
  232. line-height: 1.3;
  233. color: #333;
  234. }
  235. }
  236. .goods-item_desc {
  237. margin-top: 20rpx;
  238. .people {
  239. margin-right: 14rpx;
  240. }
  241. .desc_footer {
  242. width: 100%;
  243. display: flex;
  244. justify-content: space-between;
  245. align-items: center;
  246. position: absolute;
  247. right: 0rpx;
  248. bottom: 0rpx;
  249. min-height: 44rpx;
  250. .item-status {
  251. color: #ff1d1c;
  252. }
  253. .item-prices {
  254. padding-right: 6rpx;
  255. .price_x {
  256. margin-right: 14rpx;
  257. color: rgb(240, 60, 60);
  258. font-size: 28rpx;
  259. }
  260. .price_y {
  261. color: #999;
  262. text-decoration: line-through;
  263. font-size: 24rpx;
  264. }
  265. }
  266. }
  267. }
  268. </style>