金诚优选前端代码
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.

685 lines
18 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. <template>
  2. <view class="centent">
  3. <lf-nav :title="['分类','品牌'][current]" bgColor="#fff" :search="current == 0" @changeHeight="e => nav_height = e"></lf-nav>
  4. <view class="tabs">
  5. <view class="lf-tab"
  6. :style="{width: 100 / tabs.length +'%'}"
  7. :class="{'tab-active': current == index}"
  8. v-for="(item, index) in tabs" :key="index"
  9. @click="current = index">{{ item.name }}
  10. </view>
  11. </view>
  12. <!-- 分类 -->
  13. <view class="page" v-if="current == 0 && dataArr.length">
  14. <scroll-view class="left_view p_r" :scroll-into-view="left_tabview" scroll-y :style="{ height: autoHeight }">
  15. <block v-for="(item, index) in dataArr" :key="index">
  16. <view :class="[left_selectIndex == index ? 'left_item_s' : '', 'left_item']" :id="'left_' + index" @click="leftTap({ item, index })">{{ item.name }}</view>
  17. </block>
  18. <view class="seletItem" :id="'tab_'+ left_selectIndex" :style="{ top: left_selectIndex * 60 - 19 + 'px' }"></view>
  19. </scroll-view>
  20. <scroll-view @scroll="rightScroll" class="right_view" scroll-y :style="{ height: autoHeight }" :scroll-into-view="'left_' + right_selectIndex" scroll-with-animation>
  21. <block v-for="(item, index) in dataArr" :key="index">
  22. <view :ref="'left_' + index" class="right_item " :id="'left_' + index">
  23. <text class="right_item_title" :style="{color: left_selectIndex == index ? '#15716E' : '#555555'}">{{ item.name }}</text>
  24. <view class="right_item_view">
  25. <view class="item" v-for="(c_item, c_index) in item.sub_category" :key="c_index" @click="rightTap(c_item)">
  26. <image :src="c_item.image" :style="{ width: '100%', height: subItemW + 'px', background: '#999999' }"></image>
  27. <text class="lf-font-24">{{ c_item.name }}</text>
  28. </view>
  29. </view>
  30. </view>
  31. </block>
  32. <view class="" :style="{ height: autoScrollHeight }"><!-- 站位 --></view>
  33. </scroll-view>
  34. </view>
  35. <!-- 品牌 -->
  36. <view class="brand" v-else-if="current == 1" :style="{height: autoHeight}">
  37. <!-- 筛选 -->
  38. <view class="lf-filter-box" :style="{'z-index': filter_active == '' ? 1 : 99}">
  39. <view class="lf-filter" :class="{'lf-filter-after': filter_active == ''}">
  40. <view v-for="(value, key) in filter_list" :key="key"
  41. :class="{'filter-active': filter_active == key}"
  42. @click="clickFilterTab(key)">{{ value.name }}
  43. </view>
  44. </view>
  45. <view class="filter-modal-mask" :style="{height: otherHeight}" v-if="filter_active != ''" @click="filter_active = ''">
  46. <view class="filter-modal" @click.stop>
  47. <view class="filter-item"
  48. :class="{'filter-item-active': filter_list[filter_active].current == index}"
  49. @click="selectFilter(item, index)"
  50. v-for="(item, index) in filter_list[filter_active].list"
  51. :key="index">{{ item.name || item }}
  52. </view>
  53. </view>
  54. </view>
  55. </view>
  56. <!-- 内容 -->
  57. <scroll-view class="brand-scroll" :scroll-into-view="scrollAnchorId" :scroll-y="true" :style="{height: otherHeight}">
  58. <view class="lf-flex brand-item"
  59. :id="'anchor-'+ item.initial"
  60. @click="$url('/pages/shop/shopdetail?id='+ item.id)"
  61. v-for="(item, index) in brand_list" :key="index">
  62. <image class="img" :src="item.logo"></image>
  63. <view class="info">
  64. <view class="lf-font-36 lf-font-bold lf-color-black">{{ item.name }}</view>
  65. <view class="lf-font-24 lf-color-gray">{{ item.category }}{{ item.goods_count || 0 }}件在售</view>
  66. <view class="lf-font-24">
  67. <text class="lf-iconfont icon-dizhi lf-font-24 lf-color-primary"></text>
  68. <text class="lf-color-gray lf-m-l-10">{{ item.floor }}</text>
  69. </view>
  70. </view>
  71. </view>
  72. <view style="height: 40rpx;" v-if="brand_list.length > 0"></view>
  73. <lf-nocontent src="/static/images/empty.png" v-else></lf-nocontent>
  74. </scroll-view>
  75. <!-- 锚点定位 -->
  76. <view class="fixed-point" v-if="point_list.length">
  77. <view class="fixed-content" @touchmove="pointTouchmove">
  78. <view class="point-item"
  79. @click="pointClick"
  80. :id="'point-'+ item"
  81. :style="{height: 100 / 26 +'%'}"
  82. v-for="(item, index) in point_list"
  83. :key="index">{{ item }}
  84. </view>
  85. </view>
  86. </view>
  87. </view>
  88. <!-- 悬浮购物车入口 -->
  89. <view class="fixed-right">
  90. <view class="fixed-cart" hover-class="lf-opacity" @click="$url('/pages/store/cart/cart')">
  91. <text class="lf-iconfont icon-gouwulan icon-text"></text>
  92. <text class="tips">购物车</text>
  93. <block v-if="$isRight(car_num)">
  94. <view class="angle-mark" v-if="car_num<99">{{car_num || 0}}</view>
  95. <view class="angle-mark" v-else>99+</view>
  96. </block>
  97. </view>
  98. </view>
  99. <lf-tabbar></lf-tabbar>
  100. </view>
  101. </template>
  102. <script>
  103. import lfNav from '@/components/lf-nav/lf-nav.vue';
  104. import lfTabbar from '@/components/lf-tabbar/lf-tabbar.vue';
  105. let app = getApp();
  106. export default {
  107. data() {
  108. return {
  109. scrollH: 0,
  110. subItemW: 0,
  111. left_selectIndex: 0,
  112. right_selectIndex: 0,
  113. left_tabview: 'tab_0', // 默认和left_selectIndex对应,scroll-view定位到的左侧tab元素
  114. ttscrollH: 0, //总高度
  115. placeholderH: 0, //占位高度
  116. heighArr: [],
  117. dataArr: [],
  118. current: 1,
  119. nav_height: 0,
  120. tabs: [{
  121. name: '分类'
  122. },{
  123. name: '品牌'
  124. }],
  125. filter_list: {
  126. 'floor': {
  127. name: '楼层',
  128. list: [],
  129. current: null
  130. },
  131. 'class': {
  132. name: '分类',
  133. list: [],
  134. current: null
  135. }
  136. },
  137. filter_active: '',
  138. brand_list: [],
  139. point_list: [],
  140. scrollAnchorId: '',
  141. car_num: 0
  142. };
  143. },
  144. computed: {
  145. autoHeight(){
  146. return `calc(${this.scrollH}px - ${this.nav_height}px - 90rpx - 120rpx)`;
  147. },
  148. otherHeight(){
  149. // 屏幕可用总高度 - 导航条高度 - tabs高度 - tabbar高度 - 筛选高度
  150. return `calc(${this.scrollH}px - ${this.nav_height}px - 90rpx - 120rpx - 105rpx)`;
  151. },
  152. autoScrollHeight(){
  153. return `calc(${this.scrollH}px - ${this.nav_height}px - 90rpx - 120rpx - 300rpx)`;
  154. }
  155. },
  156. components: {
  157. lfNav,
  158. lfTabbar
  159. },
  160. onLoad(options) {
  161. // https://ext.dcloud.net.cn/plugin?id=5031
  162. this.getBrandList({load: true});
  163. if(this.$isRight(options)){
  164. this.current = options.current || 1;
  165. this.left_selectIndex = options.type || 0;
  166. this.right_selectIndex = options.type || 0;
  167. }
  168. let info = uni.getSystemInfoSync();
  169. let self = this;
  170. self.scrollH = info.screenHeight;
  171. self.subItemW = parseInt((info.screenWidth * (2 / 3) - 15 * 2 - 24) / 3);
  172. this.getCategoryList().then(() => {
  173. self.computerH();
  174. });
  175. this.getcarNum();
  176. // setTimeout(function() {
  177. // self.computerH();
  178. // }, 100);
  179. // this.createAtoZ();
  180. },
  181. methods: {
  182. getcarNum() {
  183. this.$http
  184. .get({
  185. api: 'api/shopping/cart/count',
  186. header: {
  187. Authorization: this.$cookieStorage.get('user_token')
  188. },
  189. })
  190. .then(res => {
  191. if (res.data.code == 200) {
  192. if(res.data.data == null) {
  193. this.car_num = 0;
  194. }else {
  195. this.car_num = res.data.data;
  196. }
  197. } else {
  198. wx.showModal({
  199. content: '请下拉页面刷新重试',
  200. showCancel: false
  201. });
  202. }
  203. })
  204. .catch(() => {
  205. wx.stopPullDownRefresh();
  206. wx.hideLoading();
  207. wx.showModal({
  208. content: '请求失败',
  209. showCancel: false
  210. });
  211. });
  212. },
  213. getBrandList(options = {}){
  214. let par = {};
  215. if(options.filter){
  216. let _floor = this.filter_list['floor'];
  217. let _class = this.filter_list['class'];
  218. if(_floor.current != null){
  219. par.floor = _floor.list[_floor.current].name;
  220. }
  221. if(_class.current != null){
  222. par.category_id = _class.current;
  223. }
  224. }
  225. this.$http.get({
  226. api: '/api/brand',
  227. data: par
  228. }).then(res => {
  229. console.log("getBrandList", res);
  230. if(options.load){
  231. let category = res.data.data.category;
  232. let floor = res.data.data.floor;
  233. this.filter_list['floor'].list = floor;
  234. this.filter_list['class'].list = category;
  235. }
  236. this.brand_list = res.data.data.list;
  237. this.autoCreateAtoZ(res.data.data.list);
  238. });
  239. },
  240. getCategoryList(){
  241. return new Promise((resolve, reject) => {
  242. this.$http.get({
  243. api: '/api/category/list'
  244. }).then(res => {
  245. console.log("getCategoryList", res);
  246. this.dataArr = res.data.data;
  247. resolve(res);
  248. }).catch(err => reject(err));
  249. }) },
  250. // 选择筛选项
  251. selectFilter(item, index){
  252. console.log(item, index);
  253. if(this.filter_list[this.filter_active].current == index){
  254. this.filter_list[this.filter_active].current = null;
  255. }else{
  256. this.filter_list[this.filter_active].current = index;
  257. }
  258. this.filter_active = '';
  259. this.getBrandList({filter: true});
  260. },
  261. // 生成A-Z的大写字母 自己生成的
  262. createAtoZ(){
  263. let point_list = [];
  264. for(var i=0; i<26; i++){
  265. point_list.push(String.fromCharCode(65+i));
  266. }
  267. this.point_list = point_list;
  268. },
  269. // 生成A-Z的大写字母 根据店铺类别动态生成
  270. autoCreateAtoZ(list){
  271. let letters = Array.from(new Set(list.map(item => item.initial)));
  272. letters.sort(function(a1, b1){
  273. let a = a1.toLowerCase();
  274. let b = b1.toLowerCase();
  275. if (a < b) return -1;
  276. if (a > b) return 1;
  277. return 0;
  278. })
  279. this.point_list = letters;
  280. },
  281. pointTouchmove(event){
  282. console.log(event);
  283. },
  284. pointClick(event){
  285. // let index = (event.target.id).replace('point-', '');
  286. // let letter = this.point_list[index];
  287. let letter = (event.target.id).replace('point-', '');
  288. this.scrollAnchorId = '';
  289. this.$msg(letter);
  290. this.$nextTick(() => {
  291. this.scrollAnchorId = 'anchor-'+ letter;
  292. })
  293. },
  294. leftTap: function(e) {
  295. this.left_selectIndex = e.index;
  296. this.right_selectIndex = e.index;
  297. },
  298. // 右边点击
  299. rightTap: function(item) {
  300. console.log(item);
  301. this.$url('/pages/index/onlineMall/onlineMall?id='+ item.id);
  302. },
  303. rightScroll: function(e) {
  304. let scrollH = e.detail.scrollTop + 30;
  305. let cc = this.ttscrollH - this.scrollH;
  306. let a = 0;
  307. let findInx = this.heighArr.findIndex(function(itemH, i) {
  308. a = a + itemH;
  309. return a > scrollH;
  310. });
  311. // if (scrollH >= cc) {
  312. // return;
  313. // }
  314. this.left_selectIndex = findInx;
  315. this.left_tabview = '';
  316. setTimeout(() => {
  317. this.left_tabview = 'tab_'+ findInx; // TODO 待验证是否跟随滚动
  318. }, 0);
  319. },
  320. // 计算高度
  321. computerH: function() {
  322. this.ttscrollH = 0;
  323. for (let item of this.dataArr) {
  324. let title_lineH = 49; //rpx
  325. let subNum = item.sub_category.length;
  326. let subImgH = this.subItemW; //rpx
  327. let subTitleH = 40; //rpx
  328. let rowSpecH = 8; //rpx
  329. let rowN = subNum % 3;
  330. let rowSpecNum = parseInt(subNum / 3) + parseInt(rowN > 0 ? 1 : 0);
  331. let totalRpx = title_lineH + (subImgH + subTitleH) * rowSpecNum + rowSpecH * (rowSpecNum - 1);
  332. this.heighArr.push(totalRpx);
  333. this.ttscrollH = this.ttscrollH + totalRpx;
  334. }
  335. // this.placeholderH = this.scrollH - this.heighArr[this.heighArr.length - 1];
  336. //以下方法也可以
  337. // let self=this
  338. // var selectorQuery = uni.createSelectorQuery()
  339. // selectorQuery.selectAll('.right_item').boundingClientRect(data => {
  340. // self.heighArr = data.map(item => {
  341. // return {
  342. // top: Math.round(item.top),
  343. // height: Math.round(item.height)
  344. // }
  345. // })
  346. // }).exec()
  347. // console.log('ttscrollH',this.$refs.left_0)
  348. },
  349. // 品牌筛选tab被点击
  350. clickFilterTab(key){
  351. if(this.filter_active == key){
  352. this.filter_active = '';
  353. }else{
  354. this.filter_active = key;
  355. }
  356. }
  357. }
  358. }
  359. </script>
  360. <style lang="scss" scoped>
  361. .fixed-right{
  362. position: fixed;
  363. right: 62rpx;
  364. bottom: 400rpx;
  365. width: max-content;
  366. height: max-content;
  367. z-index: 9;
  368. .fixed-cart{
  369. width: 110rpx;
  370. height: 110rpx;
  371. border-radius: 50%;
  372. background-color: #FFFFFF;
  373. border: 1rpx solid #DEDEDE;
  374. display: flex;
  375. justify-content: center;
  376. align-items: center;
  377. flex-direction: column;
  378. color: #333333;
  379. position: relative;
  380. margin-top: 30rpx;
  381. .icon-text{
  382. font-size: 50rpx;
  383. line-height: 1;
  384. }
  385. .tips{
  386. font-size: 20rpx;
  387. color: #333333;
  388. }
  389. .angle-mark{
  390. position: absolute;
  391. right: 4rpx;
  392. top: 4rpx;
  393. width: 40rpx;
  394. height: 40rpx;
  395. background-color: #15716E;
  396. border-radius: 50%;
  397. font-size: 20rpx;
  398. display: flex;
  399. justify-content: center;
  400. align-items: center;
  401. color: #FFFFFF;
  402. }
  403. }
  404. .fixed-live{
  405. background: #15716E;
  406. border-radius: 100rpx 5rpx 100rpx 100rpx;
  407. border: none;
  408. color: #FFFFFF;
  409. // animation: bounceIn 1s .2s ease both;
  410. backface-visibility:visible;
  411. transform-origin:center center;
  412. animation: demo 2s 0s infinite ease normal none ;
  413. // &>text:nth-child(1){
  414. // animation: fadeInRightBig 1s .1s ease both;
  415. // }
  416. .tips{
  417. color: #FFFFFF;
  418. }
  419. }
  420. }
  421. .page {
  422. display: grid;
  423. grid-template-columns: 1fr 2fr;
  424. grid-template-rows: auto;
  425. position: absolute;
  426. left: 0rpx;
  427. right: 0rpx;
  428. overflow: hidden;
  429. box-sizing: border-box;
  430. }
  431. .left_view {
  432. background-color: #f4f8f8;
  433. position: relative;
  434. box-sizing: border-box;
  435. // 蒙版
  436. .seletItem {
  437. height: 98px;
  438. position: absolute;
  439. top: 0rpx;
  440. left: 0rpx;
  441. z-index: 10;
  442. right: 0rpx;
  443. // background-color: rgba(255, 255, 255, 0.3);
  444. transition: top 0.2s linear;
  445. display: flex;
  446. align-items: center;
  447. box-sizing: border-box;
  448. &::before {
  449. content: '';
  450. width: 6rpx;
  451. height: 60%;
  452. background-color: #15716E;
  453. left: 0rpx;
  454. }
  455. }
  456. .left_item {
  457. display: flex;
  458. justify-content: center;
  459. align-items: center;
  460. height: 60px;
  461. margin-bottom: 0rpx;
  462. position: relative;
  463. font-size: 28rpx;
  464. box-sizing: border-box;
  465. color: #555555;
  466. }
  467. .left_item_s {
  468. background-color: #ffffff;
  469. color: #15716E;
  470. font-weight: bold;
  471. position: relative;
  472. box-sizing: border-box;
  473. }
  474. }
  475. .right_view {
  476. background-color: #ffffff;
  477. padding: 0rpx 12px;
  478. box-sizing: border-box;
  479. .right_item {
  480. .right_item_title {
  481. display: block;
  482. box-sizing: border-box;
  483. line-height: 49px;
  484. font-size: 28rpx;
  485. font-weight: bold;
  486. color: #555555;
  487. // color: #15716E;
  488. }
  489. .right_item_view {
  490. display: grid;
  491. grid-template-columns: repeat(3, 1fr);
  492. grid-template-rows: auto;
  493. grid-gap: 8px 15px;
  494. box-sizing: border-box;
  495. .item {
  496. display: flex;
  497. flex-flow: column nowrap;
  498. align-items: center;
  499. box-sizing: border-box;
  500. text {
  501. color: #333;
  502. line-height: 40px;
  503. }
  504. }
  505. }
  506. }
  507. }
  508. .tabs{
  509. width: 750rpx;
  510. height: 90rpx;
  511. display: flex;
  512. justify-content: space-between;
  513. border-bottom: 1rpx solid #e5e5e5;
  514. background-color: #FFFFFF;
  515. .lf-tab{
  516. // width: 50%;
  517. display: flex;
  518. align-items: center;
  519. justify-content: center;
  520. font-size: 28rpx;
  521. color: #777777;
  522. position: relative;
  523. &.tab-active{
  524. color: #15716E;
  525. }
  526. &.tab-active::after{
  527. content: '';
  528. position: absolute;
  529. bottom: 0;
  530. left: 50%;
  531. width: 80rpx;
  532. height: 10rpx;
  533. background-color: #15716E;
  534. border-radius: 10rpx 10rpx 0 0;
  535. margin-left: -40rpx;
  536. }
  537. }
  538. }
  539. .lf-filter-box{
  540. height: 105rpx;
  541. width: 750rpx;
  542. background-color: #F4F8F8;
  543. display: flex;
  544. justify-content: center;
  545. align-items: center;
  546. position: relative;
  547. .lf-filter{
  548. width: 300rpx;
  549. height: 65rpx;
  550. border-radius: 33rpx;
  551. border: 1rpx solid #15716E;
  552. color: #15716E;
  553. font-size: 24rpx;
  554. display: flex;
  555. align-items: center;
  556. justify-content: space-between;
  557. position: relative;
  558. overflow: hidden;
  559. &.lf-filter-after::after{
  560. content: '';
  561. position: absolute;
  562. top: 12rpx;
  563. left: calc(50% - 1rpx);
  564. width: 2rpx;
  565. height: 40rpx;
  566. background-color: #15716E;
  567. }
  568. &>view{
  569. height: 100%;
  570. width: 50%;
  571. display: flex;
  572. justify-content: space-around;
  573. align-items: center;
  574. }
  575. .filter-active{
  576. background-color: #15716E;
  577. color: #FFFFFF;
  578. }
  579. }
  580. .filter-modal-mask{
  581. position: absolute;
  582. top: 105rpx;
  583. left: 0;
  584. width: 100%;
  585. background-color: rgba(0,0,0,0.5);
  586. z-index: 99;
  587. .filter-modal{
  588. width: 750rpx;
  589. height: max-content;
  590. padding: 60rpx 32rpx;
  591. box-sizing: border-box;
  592. background-color: #FFFFFF;
  593. display: flex;
  594. flex-wrap: wrap;
  595. position: relative;
  596. z-index: 102;
  597. .filter-item{
  598. width: 215rpx;
  599. height: 65rpx;
  600. border-radius: 10rpx;
  601. border: 1rpx solid #555555;
  602. display: flex;
  603. justify-content: center;
  604. align-items: center;
  605. font-size: 28rpx;
  606. color: #555555;
  607. margin-right: 21rpx;
  608. &:nth-child(3n){
  609. margin-right: 0rpx;
  610. }
  611. &:nth-child(n+4){
  612. margin-top: 21rpx;
  613. }
  614. }
  615. .filter-item-active{
  616. background-color: #15716E;
  617. color: #FFFFFF;
  618. }
  619. }
  620. }
  621. }
  622. .brand-scroll{
  623. padding: 0rpx 32rpx;
  624. box-sizing: border-box;
  625. width: 750rpx;
  626. .brand-item{
  627. margin-bottom: 60rpx;
  628. &:nth-child(1n){
  629. margin-top: 30rpx;
  630. }
  631. }
  632. .brand-item:last-child{
  633. margin-bottom: 0rpx;
  634. }
  635. .img{
  636. width: 150rpx;
  637. height: 150rpx;
  638. border-radius: 5rpx;
  639. margin-right: 20rpx;
  640. }
  641. .info{
  642. width: 514rpx;
  643. height: 150rpx;
  644. display: flex;
  645. flex-direction: column;
  646. justify-content: space-between;
  647. }
  648. }
  649. .brand{
  650. position: relative;
  651. }
  652. .fixed-point{
  653. position: fixed;
  654. right: 0;
  655. top: 0;
  656. // top: 18vh;
  657. // height: 70vh;
  658. height: 100vh;
  659. width: 57rpx;
  660. z-index: 2;
  661. display: flex;
  662. align-items: center;
  663. .fixed-content{
  664. width: 100%;
  665. height: max-content;
  666. padding: 14rpx 0rpx;
  667. background-color: rgba(0,0,0,0.3);
  668. border-radius: 30rpx 0rpx 0rpx 30rpx;
  669. .point-item{
  670. font-size: 24rpx;
  671. color: #FFFFFF;
  672. text-align: center;
  673. }
  674. }
  675. }
  676. </style>