小程序之index索引器品牌篇实现与步骤详述

图例

index组件

.js

// 品牌索引列表
Component({
    /** 组件的属性列表 */
    properties: {
        /** 数据 */
        data: {
            type: Object,
            value: {},
            observer: function(newVal, oldVal) { this.resetRight(newVal); }
        },
        /**  配置项 */
        config: {
            type: Object,
            value: {
                animation: true, // 过渡动画是否开启
                search: true, // 是否开启搜索
                searchHeight: 45, // 搜索条高度
                suctionTop: true // 是否开启标题吸顶
            }
        }
    },

    data: {
        list: [],
        rightArr: [], // 右侧字母展示
        jumpNum: '', //跳转到那个字母
        topGroup: [], // 内容高度数据
        pos: {
            isTop: false,
            y: 0,
            oldIndex: -1
        },
        listIndex: 0,
        moveDistance: 0
    },
    ready() {},
    methods: {
        /** 数据重新渲染 */
        resetRight(data) {
            let rightArr = [];
            for (let i in data) { rightArr.push(data[i].key); }

            this.setData({
                list: data,
                rightArr
            }, () => {
                if (data.length != 0) {
                    this.queryMultipleNodes();
                }
            });
        },
        /** 右侧字母点击事件  */
        jumpMt(e) {
            let jumpNum = e.currentTarget.dataset.id;
            this.setData({ jumpNum });
        },
        /** 列表点击事件 */
        detailMt(e) {
            let detail = e.currentTarget.dataset.detail;
            let data = {type: "CLICK", data: detail};
            this.triggerEvent('brand', data);
        },
        // 获取搜索输入内容
        input(e) {
            this.value = e.detail.value;
        },
        // 基础搜索功能
        searchMt() {
            let data = {type: "SEARCH", data: this.value ? this.value : ""};
            this.triggerEvent('brand', data);
        },
        /**  监听滚动 */
        scroll(e) {
            let top = e.detail.scrollTop;
            let index = this.currentIndex(top);
            let list = this.data.topGroup;
            let distance = top - list[this.data.listIndex];
            let num = -(list[this.data.listIndex + 1] - top - 40);
            // 渲染滚动索引
            if (index !== this.data.listIndex) {
                this.setData({
                    listIndex: index,
                    moveDistance: 40,
                })
                // 如果监听到 index 的变化 ,一定要return ,否则吸顶会先变化文字后运动,会闪烁
                return;
            }
            if (num < 0) num = 0;
            if (num !== this.data.moveDistance) { this.setData({ moveDistance: num }) }
        },
        /**
         * 获取当前滚动索引
         */
        currentIndex(y) {
            let listHeight = this.data.topGroup;
            for (let i = 0; i < listHeight.length; i++) {
                let height1 = listHeight[i];
                let height2 = listHeight[i + 1];
                if (!height2 || (y >= height1 && y < height2)) {
                    return i;
                }
            }
            return 0;
        },
        /**
         * 获取节点信息
         */
        queryMultipleNodes() {
          let self = this 
            const query = wx.createSelectorQuery().in(this);
            query.selectAll('.fixed-title-hock').boundingClientRect((res) => {
                res.forEach(function(rect) { rect.top // 节点的上边界坐标 })
            }).exec((e) => {
                let arr = [];
                e[0].forEach((rect) => {
                    let num = 0;
                    if (rect.top !== 0) {
                      num = rect.top - (self.data.config.search ? self.data.config.searchHeight : 0);
                    }
                    arr.push(num);
                })
                this.setData({ topGroup: arr });
            })
        }

    }
})

.json

{
    "component": true,
    "usingComponents": {}
}

.wxml

<view class='list-warpper'>
    <!-- 搜索框 -->
    <view wx:if="{{config.search}}" class='list-search' style="height:{{config.searchHeight}}px">
        <view class='list-search-box'>
            <icon type="search" size="15" />
            <input placeholder="输入品牌名称" bindinput='input' />
        </view>
        <button class='search-button' catchtap='searchMt'>搜索</button>
    </view>
    <view wx:if="{{config.suctionTop}}" class='fiexed-box list-title' style='transform: translateY(-{{moveDistance}}px);top:{{config.search?90:0}}rpx'>
        {{list[listIndex].key  === "HOT" ? "热门品牌" : list[listIndex].key}}
    </view>

    <!-- 搜索到所有数据的时候显示 -->
    <block wx:if="{{list.length != 0 }}">
        <scroll-view class="list-scroll {{config.search?'top':''}}" 
        style=" padding-top:{{config.search?config.searchHeight:0}}px" 
        scroll-y="true" scroll-into-view="{{jumpNum}}" 
        scroll-with-animation="{{config.animation}}" 
        bindscroll="scroll">
            <!-- 主体内容显示 -->
            <view id="{{'index'+index}}" wx:for="{{list}}" wx:key="key">
                <view class='list-title fixed-title-hock'>{{item.key === "HOT" ? "热门品牌" : item.key}}</view>
                <block wx:if="{{ item.key === 'HOT' }}">
                    <view class="index-brand-box">
                        <view wx:for="{{item.list}}" 
                        wx:for-index="in" 
                        wx:key="in" 
                        wx:for-item="it"
                        class="index-brand-card"
                        data-detail="{{it}}"
                        catchtap="detailMt">
                            <view class="brand-img"><image src="{{it.img}}" class="brand-pic"></image></view>
                            <view class="brand-name"> {{it.brandName}} </view>
                        </view>
                    </view>
                </block>
                <block wx:else>
                    <view                           
                        class="index-brand-item" 
                        wx:for="{{item.list}}" 
                        wx:for-index="in" 
                        wx:key="in" 
                        wx:for-item="it"
                        data-detail="{{it}}"
                        catchtap="detailMt">
                        <view class="dflex-start">
                            <view class="brand-img"><image src="{{it.img}}" class="brand-pic"></image></view>
                            <view> {{it.brandName}} </view>
                        </view>
                    </view>
                </block>
            </view>
        </scroll-view>

        <!-- 右侧索引显示 -->
        <view class='list-right-wrapper'>
            <block wx:for="{{rightArr}}" wx:key="rightArr">
                <view wx:if="{{rightArr[index] === 'HOT'}}" class='right-item {{listIndex == index?"active":""}} icon48' data-id="{{'index'+index}}" catchtap='jumpMt'>♡</view>
                <view wx:else class='right-item {{listIndex == index?"active":""}}' data-id="{{'index'+index}}" catchtap='jumpMt'>
                    {{rightArr[index]}}
                </view>          
            </block>
        </view>
    </block>

    <!-- 没有搜索到数据的时候显示 -->
    <block wx:else>
        <view class='nodata'>没有搜索到相关的数据哦</view>
    </block>
</view>

.wxss

.list-warpper {
  position: relative;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}

.list-scroll {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}
.list-scroll.top{
  padding-top: 90rpx;
}

/* 样式控制  */
.list-title {
  background: #f5f5f5;
  color: #666;
  font-size: 36rpx;
  padding-left: 30rpx;
  height: 80rpx;
  line-height: 80rpx;
}

.list-name {
  position: relative;
  font-size: 28rpx;
  padding: 15rpx;
  padding-left: 30rpx;
  color: #999;
}

.list-name.border::after {
  content: "";
  position: absolute;
  left: 30rpx;
  right: 0;
  top: 0;
  height: 1px;
  background: #f5f5f5;
}

.list-right-wrapper {
  position: absolute;
  right: 0;
  top: 50%;
  right: 20rpx;
  padding: 20rpx 0;
  z-index: 999;
  transform: translateY(-50%);
  background: #ddd;
  border-radius: 40rpx;
  box-sizing: border-box;
  text-align: center;
}

.right-item {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 2rpx 10rpx;
  font-size: 40rpx;
  color: #666;
}
.icon48{
  font-size: 56rpx;
}
.right-item.active {
  color: #ff9900;
}
.list-search {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  width: 100%;
  height: 90rpx;
  padding: 10rpx 30rpx;
  box-sizing: border-box;
  z-index: 20;
  background: #FFF;
  border-bottom:1px #f5f5f5 solid; 
}

.search-title {
  flex-shrink: 0;
  font-size: 28rpx;
  padding-right: 10rpx;
}

.list-search-box {
  display: flex;
  align-items: center;
  padding: 0 30rpx;
  width: 100%;
  height: 70rpx;
  background: #f5f5f5;
  border-radius: 90rpx;
  font-size: 28rpx;
  box-sizing: border-box;
}

.list-search-box input {
  width: 100%;
  padding-left: 10rpx;
}

.search-button {
  flex-shrink: 0;
  height: 60rpx;
  line-height: 60rpx;
  font-size: 28rpx;
  margin-left: 10rpx;
}
.fiexed-box {
    position: absolute;
    top: 90rpx;
    z-index: 19;
    width: 100%;
}

/* new */
.index-brand-box{
    width: 100%;
    background-color: #F3F3F3;
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
    padding-left: 30rpx;
    box-sizing: border-box; 
}
.index-brand-box .index-brand-card{
  width: 148rpx;
  height: 164rpx;
  background-color: #fff;
  margin: 20rpx 30rpx 20rpx 0;
}
.index-brand-box .index-brand-card:nth-child(4n) {
  margin-right: 0;
}
.index-brand-box .index-brand-card .brand-img{
    width: 100%;
    height: 102rpx;
    padding: 10rpx;
    border-bottom: 2rpx solid #ededed;
    box-sizing: border-box;
}
.index-brand-box .index-brand-card .brand-pic{
  width: 100%;
  height: 100%;
}
.index-brand-box .index-brand-card .brand-name{
  width: 100%;
  height: 60rpx;
  line-height: 60rpx;
  text-align: center;
}
.index-brand-item{
    width: 100%;
    height: 80rpx;
    background-color: #fff;
    padding: 0 30rpx;
    border-bottom: 2rpx solid #f2f2f2;
    box-sizing: border-box;
}
.dflex-start{
  display: flex;
  justify-content: flex-start;
  align-items: center;
  color: #000;
}
.index-brand-item .brand-img{
  width: 100rpx;
  height: 80rpx;
  background-color: chartreuse;
  margin-right: 30rpx;
}

/* 无数据  */
.nodata {
  padding-top: 200rpx;
  text-align: center;
  font-size: 32rpx;
  color: #ddd;
}

调用

.js

var _default =
   {
     data: function data() {
       return {
        indexHeight: 0,
        queryBrandName: "",
        brandList: [
          { key: "HOT", list: [
            { img:"/static/img/brand/geli.png", brandName:"格力" },
            { img:"/static/img/brand/meidi.png", brandName:"美的" },
            { img:"/static/img/brand/feilipu.png", brandName:"飞利浦" },
            { img:"/static/img/brand/shenling.png", brandName:"申菱" },
            { img:"/static/img/brand/geli.png", brandName:"格力" },
            { img:"/static/img/brand/meidi.png", brandName:"美的" },
            { img:"/static/img/brand/feilipu.png", brandName:"飞利浦" },
            { img:"/static/img/brand/shenling.png", brandName:"申菱" }
          ]},
          { key: "M", list: [
            { img:"/static/img/brand/meidi.png", brandName:"美的" },
            { img:"/static/img/brand/meibo.png", brandName:"美博" }
          ]},
          {
            key: "G", list: [
              { img:"/static/img/brand/geli.png", brandName:"格力" },
              { img:"/static/img/brand/gelanshi.png", brandName:"格兰仕" }
            ]
          },
          {
            key: "T", list: [
              { img:"/static/img/brand/tcl.png", brandName:"TCL" }
            ]
          },
          {
            key: "S", list: [
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" },
              { img:"/static/img/brand/shenling.png", brandName:"申菱" }
            ]
          },
          {
            key: "Z", list: [
              { img:"/static/img/brand/zhuoli.png", brandName:"卓立" }
            ]
          },
          {
            key: "3", list: [
              { img:"/static/img/brand/sanling.png", brandName:"三菱" },
            ]
          }
        ]

       };

     },
     onLoad: function onLoad(option) {
        const _this = this;
        uni.getSystemInfo({
          success:function(res){      
            let windowHeight = res.windowHeight;
            _this.indexHeight =  windowHeight > 0 ? (windowHeight - 10) * 2 : 0;    
          }
        });
     },  
     methods: {
      toSearch: function toSearch(e){
        console.log('查询品牌名称', e)
        this.brandList = [
          {
            key: "T", list: [
              { img:"/static/img/brand/tcl.png", brandName:"TCL" }
            ]
          }
        ]
      },
      toBrand: function toBrand(e) {
        console.log('选择品牌',e.detail);
      }
     } 
    };

.json

{
    "navigationBarTitleText": "选择品牌",
    "navigationBarBackgroundColor": "#f1f1f1",
    "onReachBottomDistance": 50,
    "usingComponents": {
        "index-list": "../../../components/index-brand/index"
    }
}

.wxml

<view>
    <view class='wrapper' style="{{'height:'+indexHeight +'rpx;'}}"> 
        <index-list data="{{brandList}}" 
        data-event-opts="{{[['brand',[['toBrand',['$event']]]]]}}" bindbrand="__e"></index-list>
    </view>
</view>

.wxss

@charset "UTF-8";

page { background-color: #f1f1f1; }

.wrapper{
  width: 100%;
  background-color: #fff;
}
posted on 2024-01-14 20:07  羽丫头不乖  阅读(76)  评论(0编辑  收藏  举报