list.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. <template>
  2. <view class="content">
  3. <view class="navbar" :style="{ position: headerPosition, top: headerTop }">
  4. <view class="nav-item" :class="{ current: filterIndex === 0 }" @click="tabClick(0)">综合排序</view>
  5. <view class="nav-item" :class="{ current: filterIndex === 1 }" @click="tabClick(1)">
  6. <text>销量优先</text>
  7. <view class="p-box">
  8. <text :class="{ active: numberOrder === 1 && filterIndex === 1 }" class="iconfont iconfold"></text>
  9. <text :class="{ active: numberOrder === 2 && filterIndex === 1 }" class="iconfont iconfold xia"></text>
  10. </view>
  11. </view>
  12. <view class="nav-item" :class="{ current: filterIndex === 2 }" @click="tabClick(2)">
  13. <text>价格</text>
  14. <view class="p-box">
  15. <text :class="{ active: priceOrder === 1 && filterIndex === 2 }" class="iconfont iconfold"></text>
  16. <text :class="{ active: priceOrder === 2 && filterIndex === 2 }" class="iconfont iconfold xia"></text>
  17. </view>
  18. </view>
  19. <text class="cate-item iconfont iconapps" @click="toggleCateMask('show')"></text>
  20. </view>
  21. <view class="goods-list">
  22. <view v-for="(item, index) in goodsList" :key="index" class="goods-item" @click="navToDetailPage(item)">
  23. <!-- <view class="yuezhuan">
  24. <image src="../../static/icon/yz.png"></image>
  25. <view>
  26. <view >约赚</view>
  27. <view>¥{{ item.recommend }}</view>
  28. </view>
  29. </view> -->
  30. <view class="image-wrapper">
  31. <image :src="item.image" mode="aspectFill"></image>
  32. <!-- <view class="fanli" v-if="item.give_integral != 0">
  33. <view class="fanli-bg"><image src="https://hy.liuniu946.com/app/img/index-fanl.png" mode=""></image></view>
  34. <view class="fanli-font flex">
  35. <view class="font-left">获得响亮积分</view>
  36. <view class="font-right">{{ item.give_integral * 1 }}</view>
  37. </view>
  38. </view> -->
  39. </view>
  40. <text class="title clamp">{{ item.store_name }}</text>
  41. <view class="price-box">
  42. <text class="price">{{ item.price }}</text>
  43. <text>已售 {{ item.sales }}</text>
  44. </view>
  45. </view>
  46. </view>
  47. <uni-load-more :status="loadingType"></uni-load-more>
  48. <view class="cate-mask" :class="cateMaskState === 0 ? 'none' : cateMaskState === 1 ? 'show' : ''" @click="toggleCateMask">
  49. <view class="cate-content">
  50. <scroll-view scroll-y class="left-aside">
  51. <view v-for="item in cateList" :key="item.id" class="f-item b-b" :class="{ active: item.id === currentId }" @click.stop="tabtap(item)">{{ item.cate_name }}</view>
  52. </scroll-view>
  53. <scroll-view scroll-with-animation scroll-y class="right-aside" @scroll="asideScroll" :scroll-top="tabScrollTop">
  54. <view v-for="item in cateList" :key="item.id" class="s-list" :id="'main-' + item.id">
  55. <text class="s-item">{{ item.cate_name }}</text>
  56. <view class="t-list ">
  57. <view @click.stop="changeCate(titem)" class="t-item" v-for="titem in item.children" :key="titem.id">
  58. <image :src="titem.pic"></image>
  59. <text>{{ titem.cate_name }}</text>
  60. </view>
  61. </view>
  62. </view>
  63. </scroll-view>
  64. </view>
  65. <!-- <view class="cate-content" @click.stop.prevent="stopPrevent" @touchmove.stop.prevent="stopPrevent">
  66. <scroll-view scroll-y class="cate-list">
  67. <view v-for="item in cateList" :key="item.id">
  68. <view class="cate-item b-b two">{{ item.cate_name }}</view>
  69. <view v-for="tItem in item.children" :key="tItem.id" class="cate-item b-b" :class="{ active: tItem.id == cateId }" @click="changeCate(tItem)">
  70. {{ tItem.cate_name }}
  71. </view>
  72. </view>
  73. </scroll-view>
  74. </view> -->
  75. </view>
  76. </view>
  77. </template>
  78. <script>
  79. import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
  80. import { getProducts,getCategoryList } from '@/api/product.js';
  81. export default {
  82. components: {
  83. uniLoadMore
  84. },
  85. data() {
  86. return {
  87. currentId: '',
  88. tabScrollTop: 0,
  89. cateMaskState: 0, //分类面板展开状态
  90. headerPosition: 'fixed',
  91. headerTop: '0px',
  92. loadingType: 'more', //加载更多状态
  93. filterIndex: 0, //查询类型
  94. numberOrder: 0, //1 销量从低到高 2销量从高到低
  95. limit: 6, //每次加载数据条数
  96. page: 1, //当前页数
  97. cateId: 0, //已选三级分类id
  98. priceOrder: 0, //1 价格从低到高 2价格从高到低
  99. cateList: [], //分类列表
  100. goodsList: [] ,//商品列表
  101. fid: 0,
  102. };
  103. },
  104. onLoad(options) {
  105. // #ifdef H5
  106. // this.headerTop = document.getElementsByTagName('uni-page-head')[0].offsetHeight + 'px';
  107. // #endif
  108. this.fid = options.fid
  109. if(options.tid) {
  110. this.cateId = options.tid;
  111. }
  112. this.loadCateList(options.fid, options.sid);
  113. this.loadData();
  114. },
  115. onPageScroll(e) {
  116. //兼容iOS端下拉时顶部漂移
  117. if (e.scrollTop >= 0) {
  118. this.headerPosition = 'fixed';
  119. } else {
  120. this.headerPosition = 'absolute';
  121. }
  122. },
  123. //下拉刷新
  124. onPullDownRefresh() {
  125. this.loadData('refresh');
  126. },
  127. //监听页面是否滚动到底部加载更多
  128. onReachBottom() {
  129. this.loadData();
  130. },
  131. methods: {
  132. //加载分类
  133. async loadCateList(fid, sid) {
  134. let obj = this;
  135. getCategoryList({}).then(function(e) {
  136. obj.cateList = e.data.filter(e => {
  137. return e.id != 1;
  138. });
  139. obj.currentId = obj.cateList[0].id
  140. console.log(obj.cateList, '123456789');
  141. });
  142. },
  143. //一级分类点击
  144. tabtap(item) {
  145. console.log(item);
  146. // 判断有没有初始化页面高度对象数据
  147. if (!this.sizeCalcState) {
  148. this.calcSize();
  149. }
  150. // 获取当前点击的id
  151. this.currentId = item.id;
  152. console.log(item.top);
  153. this.tabScrollTop = item.top;
  154. console.log(this.tabScrollTop);
  155. },
  156. //右侧栏滚动
  157. asideScroll(e) {
  158. // 判断有没有初始化页面高度对象数据
  159. if (!this.sizeCalcState) {
  160. this.calcSize();
  161. }
  162. let scrollTop = e.detail.scrollTop;
  163. let box = 0; //列表包裹框高度初始化
  164. let bottom = 10; //距离页面底部多少像素左侧列表切换到最后一个一级分类
  165. // 查询当前页面对象
  166. let view = uni.createSelectorQuery().select('.content');
  167. view.fields(
  168. {
  169. id: true,
  170. dataset: true,
  171. rect: true,
  172. size: true,
  173. scrollOffset: true
  174. },
  175. function(e) {
  176. // 保存包裹框高度
  177. box = e.height;
  178. }
  179. ).exec();
  180. // 获取所有距离顶部大于滚轮距离页面高度的所有分类
  181. let tabs = this.cateList.filter(item =>( item.top-10) <= scrollTop).reverse();
  182. if (tabs.length > 0) {
  183. // 判断是否已经到达滚轮底部
  184. if (box + scrollTop + bottom >= e.detail.scrollHeight) {
  185. this.currentId = this.cateList[this.cateList.length - 1].id;
  186. } else {
  187. this.currentId = tabs[0].id;
  188. }
  189. }
  190. },
  191. //计算右侧栏每个tab的高度等信息
  192. calcSize() {
  193. let h = 0;
  194. this.cateList.forEach(item => {
  195. let view = uni.createSelectorQuery().select('#main-' + item.id);
  196. view.fields(
  197. {
  198. size: true
  199. },
  200. data => {
  201. item.top = h;
  202. h += data.height;
  203. item.bottom = h;
  204. }
  205. ).exec();
  206. });
  207. this.sizeCalcState = true;
  208. },
  209. //加载商品 ,带下拉刷新和上滑加载
  210. async loadData(type = 'add', loading) {
  211. let obj = this;
  212. let data = {
  213. page: obj.page,
  214. limit: obj.limit,
  215. sid: obj.cateId ,//分类id
  216. cid: obj.fid
  217. };
  218. //没有更多直接返回
  219. if (type === 'add') {
  220. if (obj.loadingType === 'nomore') {
  221. return;
  222. }
  223. obj.loadingType = 'loading';
  224. } else {
  225. obj.loadingType = 'more';
  226. }
  227. if (type === 'refresh') {
  228. // 清空数组
  229. obj.goodsList = [];
  230. obj.page = 1;
  231. }
  232. if (this.filterIndex == 1) {
  233. console.log(obj.salesOrder);
  234. data.salesOrder = obj.numberOrder == 1 ? 'asc' : 'desc';
  235. }
  236. if (this.filterIndex == 2) {
  237. console.log(obj.priceOrder);
  238. data.priceOrder = obj.priceOrder == 1 ? 'asc' : 'desc';
  239. }
  240. getProducts(data).then(function(e) {
  241. console.log(e.data);
  242. let arr = e.data.filter(info => {
  243. return info.cate_id != 2;
  244. });
  245. obj.goodsList = obj.goodsList.concat(arr);
  246. //判断是否还有下一页,有是more 没有是nomore
  247. if (obj.limit == e.data.length) {
  248. obj.page++;
  249. obj.loadingType = 'more';
  250. } else {
  251. obj.loadingType = 'nomore';
  252. }
  253. if (type === 'refresh') {
  254. if (loading == 1) {
  255. uni.hideLoading();
  256. } else {
  257. uni.stopPullDownRefresh();
  258. }
  259. }
  260. });
  261. },
  262. //筛选点击
  263. tabClick(index) {
  264. // 防止重复点击综合排序
  265. if (this.filterIndex === 0 && this.filterIndex === index) {
  266. return;
  267. }
  268. this.filterIndex = index;
  269. // 判断是否为销量优先
  270. if (index === 1) {
  271. this.numberOrder = this.numberOrder === 1 ? 2 : 1;
  272. }
  273. // 判断是否为价格优先
  274. if (index === 2) {
  275. this.priceOrder = this.priceOrder === 1 ? 2 : 1;
  276. }
  277. // 初始化页数
  278. this.page = 1;
  279. // 初始化数组
  280. uni.pageScrollTo({
  281. duration: 300,
  282. scrollTop: 0
  283. });
  284. this.loadData('refresh', 1);
  285. uni.showLoading({
  286. title: '正在加载'
  287. });
  288. },
  289. //显示分类面板
  290. toggleCateMask(type) {
  291. let timer = type === 'show' ? 10 : 300;
  292. let state = type === 'show' ? 1 : 0;
  293. this.cateMaskState = 2;
  294. setTimeout(() => {
  295. this.cateMaskState = state;
  296. }, timer);
  297. },
  298. //分类点击
  299. changeCate(item) {
  300. this.cateId = item.id;
  301. // 显示右侧分类
  302. this.toggleCateMask();
  303. // 滚轮返回顶部
  304. uni.pageScrollTo({
  305. duration: 300,
  306. scrollTop: 0
  307. });
  308. // 初始化查询页数
  309. this.page = 1;
  310. // 重新加载数据
  311. this.loadData('refresh', 1);
  312. uni.showLoading({
  313. title: '正在加载'
  314. });
  315. },
  316. //详情
  317. navToDetailPage(item) {
  318. let id = item.id;
  319. uni.navigateTo({
  320. url: `/pages/product/product?id=${id}`
  321. });
  322. }
  323. }
  324. };
  325. </script>
  326. <style lang="scss">
  327. page,
  328. .content {
  329. background: $page-color-base;
  330. }
  331. .content {
  332. padding-top: 96rpx;
  333. }
  334. .yuezhuan{
  335. text-align: center;
  336. z-index: 99;
  337. position: absolute;
  338. text-align: center;
  339. width:40%;
  340. }
  341. .yuezhuan image{
  342. margin-top: -12rpx;
  343. float: right;
  344. width: 120rpx;
  345. height: 120rpx;
  346. }
  347. .yuezhuan>view{
  348. width: 60rpx;
  349. top: 5rpx;
  350. height: 40rpx;
  351. text-align: center !important;
  352. font-size: 22rpx;
  353. color: #AD0B04;
  354. margin-left: 206rpx;
  355. position: absolute;
  356. }
  357. .navbar {
  358. position: fixed;
  359. left: 0;
  360. top: var(--window-top);
  361. display: flex;
  362. width: 100%;
  363. height: 80rpx;
  364. background: #fff;
  365. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.06);
  366. z-index: 10;
  367. .nav-item {
  368. flex: 1;
  369. display: flex;
  370. justify-content: center;
  371. align-items: center;
  372. height: 100%;
  373. font-size: 30rpx;
  374. color: $font-color-dark;
  375. position: relative;
  376. &.current {
  377. color: #ff4c4c;
  378. &:after {
  379. content: '';
  380. position: absolute;
  381. left: 50%;
  382. bottom: 0;
  383. transform: translateX(-50%);
  384. width: 150rpx;
  385. height: 0;
  386. border-bottom: 4rpx solid #ff4c4c;
  387. }
  388. }
  389. }
  390. .p-box {
  391. display: flex;
  392. flex-direction: column;
  393. .iconfont {
  394. display: flex;
  395. align-items: center;
  396. justify-content: center;
  397. width: 30rpx;
  398. height: 14rpx;
  399. line-height: 1;
  400. margin-left: 4rpx;
  401. font-size: 26rpx;
  402. color: #888;
  403. &.active {
  404. color: $base-color;
  405. }
  406. }
  407. .xia {
  408. transform: scaleY(-1);
  409. }
  410. }
  411. .cate-item {
  412. display: flex;
  413. justify-content: center;
  414. align-items: center;
  415. height: 100%;
  416. width: 80rpx;
  417. position: relative;
  418. font-size: 44rpx;
  419. &:after {
  420. content: '';
  421. position: absolute;
  422. left: 0;
  423. top: 50%;
  424. transform: translateY(-50%);
  425. border-left: 1px solid #ddd;
  426. width: 0;
  427. height: 36rpx;
  428. }
  429. }
  430. }
  431. /* 分类 */
  432. .cate-mask {
  433. position: fixed;
  434. left: 0;
  435. top: var(--window-top);
  436. bottom: 0;
  437. width: 100%;
  438. background: rgba(0, 0, 0, 0);
  439. z-index: 95;
  440. transition: 0.3s;
  441. .cate-content {
  442. width: 630rpx;
  443. height: 100%;
  444. background: #fff;
  445. float: right;
  446. transform: translateX(100%);
  447. transition: 0.3s;
  448. display: flex;
  449. .left-aside {
  450. flex-shrink: 0;
  451. width: 200rpx;
  452. height: 100%;
  453. background-color: #fff;
  454. }
  455. .f-item {
  456. display: flex;
  457. align-items: center;
  458. justify-content: center;
  459. width: 100%;
  460. height: 100rpx;
  461. font-size: 28rpx;
  462. color: $font-color-base;
  463. position: relative;
  464. &.active {
  465. color: $base-color;
  466. background: #f8f8f8;
  467. &:before {
  468. content: '';
  469. position: absolute;
  470. left: 0;
  471. top: 50%;
  472. transform: translateY(-50%);
  473. height: 36rpx;
  474. width: 8rpx;
  475. background-color: $base-color;
  476. border-radius: 0 4px 4px 0;
  477. opacity: 0.8;
  478. }
  479. }
  480. }
  481. .right-aside {
  482. flex: 1;
  483. overflow: hidden;
  484. padding-left: 20rpx;
  485. padding-right: 20rpx;
  486. }
  487. .s-item {
  488. display: flex;
  489. align-items: center;
  490. height: 70rpx;
  491. padding-top: 8rpx;
  492. font-size: 28rpx;
  493. color: $font-color-dark;
  494. }
  495. .t-list {
  496. display: flex;
  497. flex-wrap: wrap;
  498. border-radius: 15rpx;
  499. width: 100%;
  500. background: #fff;
  501. padding-top: 12rpx;
  502. &:after {
  503. content: '';
  504. flex: 99;
  505. height: 0;
  506. }
  507. }
  508. .t-item {
  509. flex-shrink: 0;
  510. display: flex;
  511. justify-content: center;
  512. align-items: center;
  513. flex-direction: column;
  514. width: 171rpx;
  515. font-size: 26rpx;
  516. color: #666;
  517. padding-bottom: 20rpx;
  518. image {
  519. width: 140rpx;
  520. height: 140rpx;
  521. }
  522. }
  523. }
  524. &.none {
  525. display: none;
  526. }
  527. &.show {
  528. background: rgba(0, 0, 0, 0.4);
  529. .cate-content {
  530. transform: translateX(0);
  531. }
  532. }
  533. }
  534. .cate-list {
  535. display: flex;
  536. flex-direction: column;
  537. height: 100%;
  538. .cate-item {
  539. display: flex;
  540. align-items: center;
  541. height: 90rpx;
  542. padding-left: 30rpx;
  543. font-size: 28rpx;
  544. color: #555;
  545. position: relative;
  546. }
  547. .two {
  548. height: 64rpx;
  549. color: #303133;
  550. font-size: 30rpx;
  551. background: #f8f8f8;
  552. }
  553. .active {
  554. color: $base-color;
  555. }
  556. }
  557. /* 商品列表 */
  558. .goods-list {
  559. display: flex;
  560. flex-wrap: wrap;
  561. padding: 0 30rpx;
  562. background: #fff;
  563. .goods-item {
  564. display: flex;
  565. flex-direction: column;
  566. width: 48%;
  567. padding-bottom: 40rpx;
  568. &:nth-child(2n + 1) {
  569. margin-right: 4%;
  570. }
  571. }
  572. .image-wrapper {
  573. width: 100%;
  574. height: 330rpx;
  575. border-radius: 3px;
  576. overflow: hidden;
  577. position: relative;
  578. image {
  579. width: 100%;
  580. height: 100%;
  581. opacity: 1;
  582. }
  583. .fanli {
  584. position: absolute;
  585. bottom: 0;
  586. left: 0;
  587. width: 344rpx;
  588. height: 96rpx;
  589. .fanli-bg {
  590. position: absolute;
  591. bottom: 0;
  592. left: 0;
  593. width: 344rpx;
  594. height: 96rpx;
  595. }
  596. .fanli-font {
  597. position: relative;
  598. z-index: 10;
  599. color: #ffffff;
  600. height: 96rpx;
  601. align-items: flex-end;
  602. padding: 36rpx 10rpx 10rpx;
  603. .font-left {
  604. width: 226rpx;
  605. font-size: 20rpx;
  606. font-family: PingFang SC;
  607. font-weight: 500;
  608. color: #ffffff;
  609. }
  610. .font-right {
  611. width: 118rpx;
  612. text-align: center;
  613. font-size: 36rpx;
  614. font-family: Microsoft YaHei;
  615. font-weight: bold;
  616. color: #ffffff;
  617. }
  618. }
  619. }
  620. }
  621. .title {
  622. font-size: $font-lg;
  623. color: $font-color-dark;
  624. line-height: 80rpx;
  625. }
  626. .price-box {
  627. display: flex;
  628. align-items: center;
  629. justify-content: space-between;
  630. padding-right: 10rpx;
  631. font-size: 24rpx;
  632. color: $font-color-light;
  633. }
  634. .price {
  635. font-size: $font-lg;
  636. color: #ff4c4c;
  637. line-height: 1;
  638. &:before {
  639. content: '¥';
  640. font-size: 26rpx;
  641. }
  642. }
  643. }
  644. </style>