category.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. <template>
  2. <view class="content">
  3. <view class="scroll-top flex_item">
  4. <scroll-view class="scroll-list" scroll-x>
  5. <view class="scoll-box" v-for="ls in flist" :class="{ active: ls.id === currentId }" @click="tabtap(ls.id)">
  6. <view class="scoll-img"><image :src="ls.pic"></image></view>
  7. <view class="scoll-name">{{ ls.cate_name }}</view>
  8. </view>
  9. </scroll-view>
  10. <view class="search-box" @click="Toseach">
  11. <view class="search"><image src="/static/img/img22.png" mode="scaleToFill"></image></view>
  12. <view>搜索</view>
  13. </view>
  14. </view>
  15. <view class="scroll-view flex-tpl" :style="{ height: pageHeight + 'px' }">
  16. <scroll-view scroll-y class="left-aside">
  17. <view v-for="item in slist" class="f-item" :class="{ active_top: item.id === erjiid }" @click="changeSlist(item)">{{ item.name }}</view>
  18. </scroll-view>
  19. <scroll-view :scroll-with-animation="scrollAnimation" scroll-y class="right-aside" @scroll="asideScroll" :scroll-top="tabScrollTop">
  20. <view class="image" id="topImg" @click="Tomember">
  21. <image mode="scaleToFill" class="imageLV" src="http://shicai.liuniu946.com/static/img/bpbx.jpg"></image>
  22. </view>
  23. <view class="tlist-box" v-for="ls in slist" :id="'main-' + ls.id">
  24. <view class="tlistname" v-if="ls.list">{{ ls.name }}</view>
  25. <view class="tlist-list flex_item" v-if="ls.list" v-for="item in ls.list" @click="ToDetail(item)">
  26. <view class="tlist-img">
  27. <view class="img"><image :src="item.image" :lazy-load="true" mode="scaleToFill"></image></view>
  28. <view class="stock flex_item" v-if="item.stock > 0">
  29. <image class="img" src="/static/img/img01.png" mode="scaleToFill"></image>
  30. <view class="stock-num clamp">库存剩{{ item.percent | parseIntTo }}%</view>
  31. </view>
  32. </view>
  33. <view class="sell-out" v-if="item.stock == 0"><text>已售罄</text></view>
  34. <view class="tlist-info">
  35. <view class="name clamp">{{ item.store_name }}</view>
  36. <view class="info clamp">{{ item.store_info }}</view>
  37. <view class="tipBox">
  38. <view class="tip clamp" v-if="item.keyword != ''">
  39. <text v-for="lss in item.keyword">{{ lss }}</text>
  40. </view>
  41. </view>
  42. <view class="tlist-price flex">
  43. <view class="price-box">
  44. <view class="price">
  45. ¥
  46. <text class="blod">{{ item.price }}</text>
  47. <text class="fen">/份</text>
  48. </view>
  49. <view class="vip_price">
  50. ¥{{ item.ot_price }}
  51. <text>市场价</text>
  52. </view>
  53. </view>
  54. <view class="gocar position-relative" @click.stop="Addcar(item)">
  55. <image src="/static/img/img21.png" mode="scaleToFill"></image>
  56. <view class="corner" v-if="item.cart_num > 0">
  57. <text>{{ item.cart_num }}</text>
  58. </view>
  59. </view>
  60. </view>
  61. <view class="bottom_border"></view>
  62. </view>
  63. </view>
  64. </view>
  65. </scroll-view>
  66. </view>
  67. </view>
  68. </template>
  69. <script>
  70. import { mapState, mapMutations } from 'vuex';
  71. import { category_layer } from '@/api/water.js';
  72. import { getProducts,cartAdd } from '@/api/product.js';
  73. import { getUserInfo } from '@/api/login.js';
  74. import { saveUrl } from '@/utils/loginUtils';
  75. export default {
  76. data() {
  77. return {
  78. sizeCalcState: false,
  79. tabScrollTop: 0,
  80. currentId: '', //一级选择id,
  81. erjiid: '', //二级选择id
  82. flist: [], //一级分类列表
  83. slist: [], //二级分类列表
  84. yijishow: true, //一级界面显示
  85. sanjishow: false, //三级界面隐藏
  86. bili: 1, //设置页面比例
  87. pageHeight: 0, //保存滚轮页面高度
  88. CategoryID: '',
  89. userInfo: '',
  90. onload: true, //保存数据判断是否是第一次打开页面
  91. scrollAnimation: true //是否开启动画
  92. };
  93. },
  94. onLoad(option) {
  95. if (option.spread) {
  96. uni.setStorageSync('spread', option.spread);
  97. }
  98. saveUrl();
  99. uni.showLoading({
  100. title: '加载中'
  101. });
  102. this.loadData();
  103. },
  104. onShow() {
  105. let obj = this;
  106. const categoryId = uni.getStorageSync('categoryId') || '';
  107. if (obj.currentId != categoryId && categoryId != '' && !obj.onload) {
  108. obj.tabtap(categoryId);
  109. }
  110. // 初次数据加载完毕
  111. obj.onload = false;
  112. },
  113. onReady() {
  114. // 初始化获取页面宽度
  115. uni.createSelectorQuery()
  116. .select('.content')
  117. .fields(
  118. {
  119. size: true
  120. },
  121. data => {
  122. // 获取页面百分比
  123. this.bili = data.width / 750;
  124. console.log(data, 2);
  125. this.pageHeight = data.height - 185 * this.bili;
  126. console.log(this.pageHeight, 33);
  127. }
  128. )
  129. .exec();
  130. },
  131. // #ifndef MP
  132. // 监听导航栏输入框点击事件
  133. onNavigationBarSearchInputClicked(e) {
  134. uni.navigateTo({
  135. url: '/pages/product/search'
  136. });
  137. },
  138. // #endif
  139. //下拉刷新
  140. onPullDownRefresh() {
  141. let obj = this;
  142. //监听下拉刷新动作的执行方法,每次手动下拉刷新都会执行一次
  143. setTimeout(function() {
  144. obj.loadData();
  145. uni.stopPullDownRefresh(); //停止下拉刷新动画
  146. }, 1000);
  147. },
  148. filters: {
  149. parseIntTo(percent) {
  150. percent = +percent * 100;
  151. if (percent % 1 === 0) {
  152. return percent;
  153. } else {
  154. percent = percent.toFixed(1);
  155. return percent;
  156. }
  157. }
  158. },
  159. computed: {
  160. ...mapState(['GetInfo']),
  161. },
  162. methods: {
  163. // 载入数据
  164. async loadData() {
  165. let obj = this;
  166. obj.loading = true;
  167. category_layer({})
  168. .then(({ data }) => {
  169. obj.flist = data.list;
  170. obj.currentId = data.first;
  171. obj.erji();
  172. })
  173. .catch(err => {
  174. console.log(err);
  175. });
  176. },
  177. // 二级数据加载
  178. erji() {
  179. let obj = this;
  180. getProducts({
  181. cid: obj.currentId,
  182. flag: 1,
  183. type: 1
  184. })
  185. .then(({ data }) => {
  186. // let newArr = [];
  187. // let arr = [];
  188. obj.slist = data.map(function(s) {
  189. // if(s.list){
  190. // arr = s.list;
  191. // newArr.push(...arr);
  192. // }
  193. // if (s.list) {
  194. // s.list = s.list.map(function(e) {
  195. // e.percent = +e.percent*100;
  196. // console.log(e.percent )
  197. // if (e.percent % 1 === 0) {
  198. // return e;
  199. // } else {
  200. // e.percent = e.percent.toFixed(1);
  201. // console.log(e.percent )
  202. // return e;
  203. // }
  204. // });
  205. // }
  206. return s;
  207. });
  208. obj.erjiid = obj.slist[0].id;
  209. obj.$nextTick(e => {
  210. // 重新开启动画效果
  211. uni.hideLoading();
  212. // 数据加载完成后重新计算高度
  213. obj.calcSize()
  214. obj.scrollAnimation = true;
  215. });
  216. // obj.erjiid = newArr[0].id;
  217. // data.forEach(function(e) {
  218. // if (e.list) {
  219. // obj.tlist = obj.tlist.concat(e.list);
  220. // }
  221. // });
  222. })
  223. .catch(err => {
  224. uni.hideLoading();
  225. console.log(err);
  226. });
  227. },
  228. //分享
  229. // #ifdef MP
  230. onShareAppMessage: function(res) {
  231. let userInfo = uni.getStorageSync('userInfo');
  232. // 来自页面内分享按钮
  233. let pages = getCurrentPages();
  234. // 获取当前页面
  235. let page = pages[pages.length - 1];
  236. let path = '/pages/category/category?';
  237. // 保存邀请人
  238. path += 'spread=' + userInfo.uid;
  239. let data = {
  240. path: path,
  241. imageUrl: this.GetInfo.img,
  242. title: this.GetInfo.title
  243. };
  244. console.log(data)
  245. return data;
  246. },
  247. // #endif
  248. // 加载三级数据
  249. // sanji() {
  250. // let obj = this;
  251. // getProducts({
  252. // sid: obj.erjiid
  253. // })
  254. // .then(({ data }) => {
  255. // obj.tlist = data;
  256. // })
  257. // .catch(err => {
  258. // console.log(err);
  259. // });
  260. // },
  261. //加入购物车
  262. Addcar(item) {
  263. let obj = this;
  264. cartAdd({
  265. cartNum: '1', //商品数量
  266. uniqueId: '', //商品标签
  267. new: 0, //商品是否新增加到购物车1为不加入0为加入
  268. mer_id: '',
  269. productId: item.id //商品编号
  270. })
  271. .then(function(e) {
  272. uni.showToast({
  273. title: '成功加入购物车',
  274. type: 'top',
  275. duration: 500,
  276. icon: 'none'
  277. });
  278. obj.erji();
  279. })
  280. .catch(e => {
  281. console.log(e);
  282. });
  283. },
  284. //一级分类点击
  285. tabtap(item) {
  286. uni.showLoading({
  287. title: '加载中'
  288. });
  289. let obj = this;
  290. // 关闭动画效果
  291. obj.scrollAnimation = false;
  292. // 设置顶部高度为0
  293. obj.$nextTick(function(){
  294. // 等待渲染完毕在执行高度修改否则动画效果依然存在
  295. obj.tabScrollTop = 0;
  296. })
  297. // 获取当前点击的id
  298. obj.currentId = item;
  299. // 二级分类分类保存id
  300. obj.erjiid = item;
  301. uni.setStorageSync('categoryId', item);
  302. // 重置分类滚轮绑定高度
  303. obj.sizeCalcState = false;
  304. // 加载二级方法
  305. setTimeout(function() {
  306. obj.erji();
  307. });
  308. },
  309. //二级点击
  310. changeSlist(item) {
  311. let obj = this;
  312. // 判断有没有初始化页面高度对象数据
  313. console.log(obj.sizeCalcState)
  314. if (!obj.sizeCalcState) {
  315. obj.calcSize();
  316. }
  317. // 获取当前点击的id
  318. obj.erjiid = item.id;
  319. let index = obj.slist.findIndex(sitem => sitem.id === item.id);
  320. setTimeout(function() {
  321. obj.tabScrollTop = obj.slist[index].top;
  322. }, 10);
  323. },
  324. // 商品详情页
  325. ToDetail(item) {
  326. if (item.stock < 1) {
  327. this.$api.msg('该商品已售罄');
  328. } else {
  329. uni.navigateTo({
  330. url: `/pages/product/product?id=` + item.id
  331. });
  332. }
  333. },
  334. Tomember() {
  335. uni.navigateTo({
  336. url: '/pages/groupBooking/index'
  337. });
  338. // this.$api.msg('暂未开通!');
  339. // this.userInfo = uni.getStorageSync('userInfo');
  340. // if(this.userInfo == ''){
  341. // userinfo({})
  342. // .then(({ data }) => {
  343. // this.userInfo = data;
  344. // })
  345. // .catch(e => {});
  346. // }else{
  347. // if (this.userInfo.level_info.grade > 99) {
  348. // uni.navigateTo({
  349. // url: '/pages/user/renew'
  350. // });
  351. // }
  352. // if (this.userInfo.level_info.grade < 100) {
  353. // uni.navigateTo({
  354. // url: '/pages/user/card'
  355. // });
  356. // }
  357. // }
  358. },
  359. Toseach() {
  360. uni.navigateTo({
  361. url: `/pages/product/search`
  362. });
  363. },
  364. //右侧栏滚动
  365. asideScroll(e) {
  366. // 判断有没有初始化页面高度对象数据
  367. if (!this.sizeCalcState) {
  368. this.calcSize();
  369. }
  370. let scrollTop = e.detail.scrollTop;
  371. let box = 0; //列表包裹框高度初始化
  372. let bottom = 10; //距离页面底部多少像素左侧列表切换到最后一个一级分类
  373. // 查询当前页面对象
  374. let view = uni.createSelectorQuery().select('.right-aside');
  375. view.fields(
  376. {
  377. id: true,
  378. dataset: true,
  379. rect: true,
  380. size: true,
  381. scrollOffset: true
  382. },
  383. function(e) {
  384. // 保存包裹框高度
  385. box = e.height;
  386. }
  387. ).exec();
  388. // 获取所有距离顶部大于滚轮距离页面高度的所有分类
  389. let tabs = this.slist.filter(item => item.top <= scrollTop).reverse();
  390. if (tabs.length > 0) {
  391. // 判断是否已经到达滚轮底部
  392. if (box + scrollTop + bottom >= e.detail.scrollHeight) {
  393. this.erjiid = this.slist[this.slist.length - 1].id;
  394. } else {
  395. this.erjiid = tabs[0].id;
  396. }
  397. }
  398. },
  399. //计算右侧栏每个tab的高度等信息
  400. calcSize() {
  401. let h = this.bili * 215;
  402. this.slist.forEach(item => {
  403. let view = uni.createSelectorQuery().select('#main-' + item.id);
  404. view.fields(
  405. {
  406. size: true
  407. },
  408. data => {
  409. item.top = Math.ceil(h);
  410. h += data.height;
  411. item.bottom = Math.ceil(h);
  412. }
  413. ).exec();
  414. });
  415. this.sizeCalcState = true;
  416. }
  417. }
  418. };
  419. </script>
  420. <style lang="scss">
  421. page {
  422. background-color: #ffffff;
  423. height: 100%;
  424. .content {
  425. background-color: #ffffff;
  426. height: 100%;
  427. }
  428. }
  429. //顶部分类
  430. .scroll-top {
  431. width: 100%;
  432. font-size: 24rpx;
  433. height: 170rpx;
  434. padding: 0px 25rpx;
  435. box-shadow: 0 1px 5px rgba(0, 0, 0, 0.06);
  436. .scroll-list {
  437. width: 80%;
  438. overflow: hidden;
  439. white-space: nowrap;
  440. .scoll-box {
  441. margin-right: 15rpx;
  442. text-align: center;
  443. display: inline-block;
  444. .scoll-img {
  445. width: 130rpx;
  446. height: 85rpx;
  447. border-radius: 100%;
  448. image {
  449. width: 85rpx;
  450. height: 100%;
  451. border-radius: 100%;
  452. }
  453. }
  454. .scoll-name {
  455. padding-top: 15rpx;
  456. }
  457. &.active {
  458. color: $base-color;
  459. }
  460. }
  461. }
  462. .search-box {
  463. text-align: center;
  464. margin-left: 25rpx;
  465. .search {
  466. width: 85rpx;
  467. height: 85rpx;
  468. border-radius: 100%;
  469. margin-bottom: 15rpx;
  470. image {
  471. width: 100%;
  472. height: 100%;
  473. border-radius: 100%;
  474. }
  475. }
  476. }
  477. }
  478. .flex-tpl {
  479. display: flex;
  480. justify-content: space-between;
  481. }
  482. .scroll-view {
  483. margin-top: 15rpx;
  484. .left-aside {
  485. width: 190rpx;
  486. background-color: rgba(245, 246, 248, 1);
  487. overflow: hidden;
  488. height: 100%;
  489. display: block;
  490. overflow-y: scroll;
  491. }
  492. }
  493. .f-item {
  494. display: flex;
  495. align-items: center;
  496. justify-content: center;
  497. width: 100%;
  498. height: 100rpx;
  499. font-size: 24rpx;
  500. color: $font-color-base;
  501. position: relative;
  502. &.active_top {
  503. background: #ffffff;
  504. font-size: 26rpx;
  505. font-weight: 500;
  506. color: rgba(51, 51, 51, 1);
  507. &:before {
  508. content: '';
  509. position: absolute;
  510. left: 0;
  511. top: 50%;
  512. transform: translateY(-50%);
  513. height: 36rpx;
  514. width: 8rpx;
  515. background-color: $base-color;
  516. border-radius: 0 4px 4px 0;
  517. opacity: 0.8;
  518. }
  519. }
  520. }
  521. .right-aside {
  522. overflow: hidden;
  523. padding: 0rpx 20rpx;
  524. height: 100%;
  525. display: block;
  526. overflow-y: scroll;
  527. width: 100%;
  528. .image {
  529. width: 100%;
  530. padding-bottom: 15rpx;
  531. .imageLV {
  532. width: 100%;
  533. height: 200rpx;
  534. }
  535. }
  536. .tlist-box {
  537. .tlistname {
  538. font-size: 24rpx;
  539. padding: 25rpx 25rpx;
  540. border-top: 2rpx solid rgba(238, 238, 238, 1);
  541. border-bottom: 2rpx solid rgba(238, 238, 238, 1);
  542. }
  543. .tlist-list.flex_item {
  544. height: 0;
  545. min-height: 280rpx;
  546. align-items: stretch;
  547. }
  548. .tlist-list {
  549. padding: 25rpx 0rpx;
  550. position: relative;
  551. .tlist-img {
  552. width: 169rpx;
  553. position: relative;
  554. .img {
  555. width: 169rpx;
  556. height: 169rpx;
  557. image {
  558. width: 169rpx;
  559. height: 169rpx;
  560. }
  561. }
  562. .stock {
  563. margin-top: 13rpx;
  564. font-size: 26rpx;
  565. background: #fff1ee;
  566. width: 100%;
  567. color: #fb4912;
  568. padding: 6rpx 0;
  569. border-radius: 5rpx;
  570. justify-content: center;
  571. align-items: center;
  572. position: absolute;
  573. left: 0;
  574. bottom: 0;
  575. .img {
  576. width: 20rpx;
  577. height: 20rpx;
  578. flex-shrink: 0;
  579. }
  580. .stock-num {
  581. padding-left: 7rpx;
  582. font-size: 22rpx;
  583. border-radius: 5rpx;
  584. height: 32rpx;
  585. line-height: 32rpx;
  586. }
  587. }
  588. }
  589. .sell-out {
  590. position: absolute;
  591. width: 169rpx;
  592. height: 190rpx;
  593. background: rgba(255, 255, 255, 0.4);
  594. text-align: center;
  595. text {
  596. line-height: 190rpx;
  597. background: rgba(0, 0, 0, 0.5);
  598. color: #ffffff;
  599. padding: 10rpx 25rpx;
  600. border-radius: 25rpx;
  601. font-size: 20rpx;
  602. }
  603. }
  604. .tlist-info {
  605. font-size: 24rpx;
  606. width: 61%;
  607. padding-left: 25rpx;
  608. padding-right: 25rpx;
  609. height: 100%;
  610. position: relative;
  611. line-height: 1;
  612. .bottom_border {
  613. position: absolute;
  614. border-bottom: 1px solid #eeeeee;
  615. left: 25rpx;
  616. bottom: 0;
  617. height: 2rpx;
  618. width: 100%;
  619. }
  620. .name {
  621. height: 28rpx;
  622. color: #141821;
  623. font-weight: 500;
  624. font-size: 26rpx;
  625. }
  626. .info {
  627. margin: 10rpx 0rpx;
  628. height: 30rpx;
  629. color: #979797;
  630. font-size: 24rpx;
  631. }
  632. .tipBox {
  633. height: 50rpx;
  634. }
  635. .tip {
  636. padding: 10rpx 0rpx;
  637. text {
  638. border: 2rpx solid #ff1a27;
  639. color: #ff1a27;
  640. border-radius: 5rpx;
  641. font-size: 18rpx;
  642. padding: 5rpx 10rpx;
  643. margin-right: 15rpx;
  644. }
  645. }
  646. .tlist-price {
  647. position: absolute;
  648. left: 25rpx;
  649. bottom: 27rpx;
  650. width: 100%;
  651. .price-box {
  652. .price {
  653. padding-bottom: 15rpx;
  654. color: #ff1a27;
  655. .blod {
  656. font-size: 35rpx;
  657. font-weight: bold;
  658. }
  659. .fen {
  660. color: #838691;
  661. }
  662. }
  663. .vip_price {
  664. color: #2dbd59;
  665. font-size: 26rpx !important;
  666. text {
  667. background: linear-gradient(45deg, rgba(21, 197, 52, 1), rgba(21, 197, 52, 1));
  668. border-radius: 5rpx;
  669. color: #ffffff;
  670. padding: 0rpx 10rpx;
  671. margin-left: 10rpx;
  672. font-size: 20rpx !important;
  673. }
  674. }
  675. }
  676. .gocar {
  677. width: 50rpx;
  678. height: 50rpx;
  679. align-self: flex-end;
  680. image {
  681. width: 100%;
  682. height: 100%;
  683. }
  684. }
  685. }
  686. }
  687. }
  688. }
  689. .tlist {
  690. .cate {
  691. padding: 25rpx 0rpx;
  692. color: #666666;
  693. font-size: 24rpx;
  694. }
  695. }
  696. }
  697. .s-item {
  698. display: flex;
  699. align-items: center;
  700. height: 70rpx;
  701. padding-top: 8rpx;
  702. font-size: 28rpx;
  703. color: $font-color-dark;
  704. }
  705. .t-list {
  706. display: flex;
  707. flex-wrap: wrap;
  708. border-radius: 15rpx;
  709. width: 100%;
  710. background: #fff;
  711. padding-top: 12rpx;
  712. &:after {
  713. content: '';
  714. flex: 99;
  715. height: 0;
  716. }
  717. }
  718. .t-item {
  719. flex-shrink: 0;
  720. display: flex;
  721. justify-content: center;
  722. align-items: center;
  723. flex-direction: column;
  724. width: 171rpx;
  725. font-size: 26rpx;
  726. color: #666;
  727. padding-bottom: 20rpx;
  728. image {
  729. width: 140rpx;
  730. height: 140rpx;
  731. }
  732. }
  733. </style>