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

577 lines
16 KiB

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