Vue中使用better-scroll插件实现移动端字母侧边导航滚动条(包括列表按首字母排序、点击导航跳转位置)完整实现

在这里插入图片描述
实现效果如上图所示。
首先引入better-scroll的组件:
1、npm安装better-scroll插件。

npm install--save better-scroll

2、在文件中引入better-scroll。

import BScroll from 'better-scroll';

排序使用的是pinyin的包,将字符串转成拼音,就可以通过字符串截取取出拼音首字母,这样就可以进行首字母排序了。
1、安装方式:

npm install pinyin

2、在应用的.vue文件中单独引入

var pinyin = require("pinyin");

准备工作结束,现在开始正式编写页面:

<template>
  <div id="Container" style="min-height: 100%">
    <van-sticky>
      <searchbox />
    </van-sticky>
    <van-row class="main" style="display: flex">
      <div class="bar">
        <sidebar> </sidebar>
      </div>
      <van-row class="listzhanwei" style="margin-top: 0.5rem">
        <div class="scrollFather" ref="listView">
          <ul class="brandlist">
            <div
              class="rowclass"
              v-for="(group, k) in searchlist"
              :key="k"
              ref="listGroup"
            >
              <van-row>
                <div class="first">
                  {{ group.first }}
                </div>
              </van-row>
              <van-row>
                <van-col span="12" v-for="(item, i) in group.value" :key="i">
                </van-col>
              </van-row>
            </div>
            <div class="zhanwei" style="height: 3.2rem"></div>
          </ul>
        </div>
      </van-row>
    </van-row>
 <div class="list-shortcut" ref="firstList">
      <ul>
        <li
          ref="firstItem"
          v-for="(item, index) in shortcutList"
          class="item"
          :data-index="index"
          :key="index"
          @click="scrollToIndex"
        >
          {{ item }}
        </li>
      </ul>
    </div> 
  </div>
</template>

样式也附送一下:

<style >
body {
  margin: 0;
  padding: 0;
}
.scrollFather {
  /* overflow-y: scroll; */
  overflow: hidden;
  position: absolute;
  height: 100%;
  width: 83%;
}
.first {
  font-weight: bold;
  line-height: 0.7rem;
  background-color: #ffffff;
  font-size: 0.6rem;
  /* margin-top: 0.5rem; */
  margin-bottom: 0.6rem;
  margin-left: 0.5rem;
  display: flex;
  justify-content: flex-start;
}
.list-shortcut {
  position: fixed;
  z-index: 30;
  right: 0.2rem;
  color: black;
  top: 50%;
  transform: translateY(-50%);
  text-align: center;
  background: #fff;
  /* user-select: none; */
  cursor: pointer;
}

.list-shortcut .item {
  line-height: 0.8rem;
  color: #8a8a8f;
  font-size: 0.7rem;
}
</style>

接下来是功能实现。

<script>
import pinyin from "pinyin";
// 导入列表接口
import Better from "better-scroll";
export default {
  components: {
    sidebar: sidebar,
    searchbox: searchbox,
  },
  data() {
    return {
      searchlist: [],
      //滚动条
      shortcutList: [],
    };
  },
  created() {
    this.getInfo();
  },
  computed: {},
  watch: {},
  methods: {
    async getInfo() {
      let brandJson = {};
      //获取自己的信息列表
      const res = await getInfoList();
      console.log(res);
      for (let i = 0; i < res.data.length; i++) {
        //遍历数组,拿到品牌名称
        let brandName = res.data[i].brand;
        //取全部品牌的首字母,这里截取首字母的第一位且大写
        let fristName = pinyin(brandName, {
          style: pinyin.STYLE_NORMAL,
          heteronym: true,
        })[0][0]
          .substring(0, 1)
          .toUpperCase();
        //给原json添加首字母键值对
        res.data[i].first = fristName;
        if (!brandJson[fristName]) {
          this.shortcutList.push(fristName);
          brandJson[fristName] = {
            first: fristName,
            value: [],
          };
        }
        brandJson[fristName].value.push(res.data[i]);
      }
      // 转为数组
      var ret = [];
      for (var k in brandJson) {
        var val = brandJson[k];
        ret.push(val);
      }
      // 对首字母排序
      ret.sort((a, b) => {
        return a.first.charCodeAt(0) - b.first.charCodeAt(0);
      });
      this.shortcutList.sort((a, b) => {
        return a.charCodeAt(0) - b.charCodeAt(0);
      });
      // 对每个品牌进行排序
      ret.map((v) => {
        v.value.sort((a, b) => {
          return a["brand"].localeCompare(b["brand"]);
        });
      });
      this.searchlist = ret;
    },
    scrollToIndex(e) {
      console.log(this.$refs.listGroup);
      let index = this.shortcutList.indexOf(e.target.innerText);
      this.$nextTick(() => {
        this.scroll.scrollToElement(this.$refs.listGroup[index]);
        if (index == this.$refs.listGroup.length - 1) {
          this.scroll.scrollBy(0, -100);
        }
      });
    },
  },
  mounted() {
    setTimeout(() => {
      this.scroll = new Better(this.$refs.listView, {
        probeType: 3,
        click: true,
      });
      this.scroll.refresh();
    }, 200);
  },
</script>

但是以上方法会有一个问题,字母侧边导航栏只能展示已有数据的存在的首字母,是根据自己的数据定的,不一定是26个字母全有。想要另外导入26个字母可以使用:

const letterlist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");

但是当点击列表中没有的字母时,页面不会有任何变化且后台会报错。想到一个迂回的方法,也就是当点击的字母是不存在的首字母项时,检查前一位字母是否存在对应的信息,依此类推。修改后的代码(只贴出修改的部分):
导航条的输出:

<div class="list-shortcut" ref="firstList">
      <ul>
        <li
          ref="firstItem"
          v-for="(item, index) in shortcutList"
          class="item"
          :data-index="index"
          :key="index"
          @click="scrollToIndex(item.index)"
        >
          {{ item.value }}
        </li>
      </ul>
    </div>

JS:

data() {
    return {
      searchlist: [],
      //滚动条
      shortcutList: {},
      hascutList: [],
    };
  },
  methods: {
    async getInfo() {
      //获取自己的信息列表
      const sampleres = await getInfo();
      const brandList = sampleres.result.data;
      let brandJson = {};
      const letterlist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
      for (let i = 0; i < brandList.length; i++) {
        //遍历数组,拿到列表项的名称
        let brandName = brandList[i].c_name;
        //取全部的首字母,这里截取首字母的第一位且大写
        let fristName = pinyin(brandName, {
          style: pinyin.STYLE_NORMAL,
          heteronym: true,
        })[0][0]
          .substring(0, 1)
          .toUpperCase();
        //给原json添加首字母键值对
        brandList[i].first = fristName;
        if (!brandJson[fristName]) {
          this.hascutList.push(fristName);
          brandJson[fristName] = {
            first: fristName,
            value: [],
          };
        }
        brandJson[fristName].value.push(brandList[i]);
      }
      // 转为数组
      var ret = [];
      for (var k in brandJson) {
        var val = brandJson[k];
        ret.push(val);
      }
      // 对首字母排序
      ret.sort((a, b) => {
        return a.first.charCodeAt(0) - b.first.charCodeAt(0);
      });
      this.hascutList.sort((a, b) => {
        return a.charCodeAt(0) - b.charCodeAt(0);
      });
      // 对每个信息项进行排序
      ret.map((v) => {
        v.value.sort((a, b) => {
          return a["c_name"].localeCompare(b["c_name"]);
        });
      });
      //初始化字母导航条所有的项,加入index字段
      letterlist.forEach((e, index) => {
        this.shortcutList[index] = {
          index: index,
          value: e,
        };
      });
      //遍历26个字母
      letterlist.forEach((e, k) => {
        //如果列表分类后首字母存在,则把first的index赋值给导航条中的字母项,这个必须在前
        if (this.hascutList.indexOf(e) >= 0) {
          this.hascutList.forEach((ele, index) => {
            if (ele === e) {
              this.shortcutList[k] = {
                index: index,
                value: e,
              };
              return;
            }
          });
        } else {
          //如果列表分类后首字母不存在,则把前一项存在的字母的index赋值给当前的字母项
          if (this.hascutList.indexOf(e) < 0) {
            // console.log(letterlist[k]);
            for (let i = 1; i < 26; i++) {
              if (this.hascutList.indexOf(letterlist[k - i]) >= 0) {
                // console.log(i + "_" + this.shortcutList[k - i].value);
                this.shortcutList[k] = {
                  index: this.shortcutList[k - i].index,
                  value: e,
                };
                return;
              }
            }
          }
        }
      });
      this.searchlist = ret;
    },
    //滚动条跳转
    scrollToIndex(e) {
      let index = e;
      this.$nextTick(() => {
        this.scroll.scrollToElement(this.$refs.listGroup[index]);
        if (index == this.$refs.listGroup.length - 1) {
          this.scroll.scrollBy(0, -100);
        }
      });
    },
  },

其实是比较笨的方法,如果有更简单的方法欢迎分享~

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值