固定在侧边的字母索引列表,新建一个aIndex.vue组件
<script>
export default {
name: 'a-index',
props: {
//字母列表
List: {
type: Array,
default: () => []
},
//当前字母索引
activeId: {
type: String,
default: () => ''
}
},
methods: {
//点击字母
go(letter) {
this.$emit('go', letter)
},
}
}
</script>
<template>
<div class="alphabet">
<dl
class="letter-list"
>
<dt
v-for="item in List"
:key="item.letter"
@click.capture="go(item.letter)"
class="letter-item"
:class="{
current: activeId == item.letter
}"
>
{{ item.letter }}
</dt>
</dl>
</div>
</template>
<style lang="less" scoped>
.alphabet{
position: absolute;
top: 50%;
right: 7px;
transform: translateY(-50%);
z-index: 100;
.letter-list {
color: #C2CCD0;
font-size: 12px;
.letter-item {
height: 15px;
line-height: 15px;
width: 15px;
font-size: 12px;
text-align: center;
position: relative;
cursor: pointer;
&.current {
color: #4598F0;
background: #F1F4F9;
border-radius: 50%;
}
}
}
}
</style>
处理要展示的数据,新建一个utils.js,这里需要安装pinyin插件 npm install pinyin-pro --save
export const AlphabetMap = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z".split(
","
);
//初始化数据,按首字母分离
export function initList(list) {
let letters = []; // 数据列表
let listsMap = {}; // 每一个索引指向一个数组,这个数组存储该字母索引的朋友项
list.forEach(item => {
let name = item.jobName.trim()
let firstLetter = ''
if(name[0]) {
firstLetter = pinyin(name[0], {toneType: 'none'}).charAt(0).toUpperCase();
}
if (!AlphabetMap.includes(firstLetter)) {
firstLetter = "#";
}
listsMap[firstLetter] || (listsMap[firstLetter] = []);
listsMap[firstLetter].push(item);
});
for (let letter in listsMap) {
letters.push({
letter: letter,
children: listsMap[letter]
});
}
// 按首字母排序
// 因此这里需要通过小于号比较
letters.sort((a, b) => {
return a.letter.localeCompare(b.letter);
});
// 将#栏放入最后
if(letters[0].letter == '#') {
letters.push(letters.shift());
}
return letters;
}
在页面中使用,引入这两个文件
注意: 实现滚动效果时要记得不能使用display:none;否则会获取不到滚动距离!!!
<script>
import {AIndex} from 'packages/a-index'
import { initList } from './utils'
export default {
components: {
AIndex
},
data () {
return {
activeId: '',
postsList: [], //需要处理的数据
}
},
mounted() {
this.postsList = initList(postsList)
let timeId;
window.addEventListener('scroll', () => {
clearTimeout(timeId);
timeId = setTimeout(() => {
this.scrollToTop();
});
} , true);
},
methods: {
//点击滚动到当前字母
goTop(letter) {
this.$el.querySelector('[id="' + letter + '"]').scrollIntoView();
},
// 监听页面元素滚动,改变导航栏选中
scrollToTop() {
// dom滚动位置
let scrollTop = document.querySelector("#letterBlocks").scrollTop;
this.postsList.map((v,i) => {
// 获取监听元素距离视窗顶部距离
let offsetTop = document.getElementById(v.letter).offsetTop;
// 获取监听元素本身高度
let scrollHeight = document.getElementById(v.letter).scrollHeight;
// 如果 dom滚动位置 >= 元素距离视窗距离 && dom滚动位置 <= 元素距离视窗距离+元素本身高度
// 则表示页面已经滚动到可视区了。
if (scrollTop >= offsetTop && scrollTop<=(offsetTop+scrollHeight)) {
this.activeId = v.letter;
}
})
//如果滑到顶部,去掉字母悬浮样式
if(scrollTop <= 40) {
this.activeId = ''
}
},
},
destroyed() {
window.removeEventListener('scroll')
}
}
</script>
<template>
<div class="list">
<div class="list-post" ref="letterBlocks" id="letterBlocks">
<div v-for="item in postsList" :key="item.letter" :id="item.letter" :class="{top: activeId == item.letter}">
<div class="letter" :class="{active: activeId == item.letter}">
{{ item.letter }}
</div>
<div v-for="i in item.children" :key="i.id">
<span>i.jobName</span>
<div>
</div>
<a-index class="a-index" :activeId="activeId" :List="postsList" @go="goTop"></a-index>
</div>
</div>
</template>
<style lang="less" scoped>
.list{
height: 100%;
position: relative;
.list-post {
padding-right: 22px;
height: 100%;
overflow-y: overlay;
.letter {
padding-left: 16px;
width: 100%;
height: 32px;
line-height: 32px;
font-size: 14px;
color: #768893;
}
.active {
background: #fff;
box-shadow: 0px 3px 4px 0px #0C213F0F;
position: absolute;
top: 0;
z-index: 99;
}
}
.top {
padding-top: 32px;
}
}
</style>