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

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