效果图
代码实现
<view class="main-container">
<!-- 成员列表 -->
<scroll-view
class="member-list"
:style="computedHeight"
:scroll-y="true"
:enable-back-to-top="true"
:scroll-with-animation="true"
:scroll-into-view="toView"
:scroll-top="scrollTop"
@scroll="onScroll"
>
<view class="list-wrap">
<view class="category">发起人</view>
<view class="list-item">
<image class="list-item-img" :src="initiatorInfo.headUrl" />
<view class="list-item-name">{{ initiatorInfo.nickName }}</view>
</view>
</view>
<view class="list-wrap last-wrap" v-for="(v, i) in memberList" :key="i" :id="v.sign == '#' ? 'intoView_' : 'intoView' + v.sign">
<view class="category">{{ v.sign }} ({{ v.list.length }}人)</view>
<view class="list-item" v-for="item in v.list" :key="item.numberId">
<image class="list-item-img" :src="item.headUrl" />
<view class="list-item-name">{{ item.nickName }}</view>
<view class="list-item-btn" @click="handleRemove(item)" v-if="item.userType != 'System'">将TA移出</view>
</view>
</view>
</scroll-view>
<!-- 右侧字母栏 -->
<scroll-view class="letter-list">
<view :class="['letter-item', activeLetter == '↑' ? 'active' : '']" @click.stop="toTop" @touchend.stop="handleTouchEnd">↑</view>
<!-- <view :class="['letter-item', activeLetter == '☆' ? 'active-item' : '']" @click="toStar">☆</view> -->
<view :class="['letter-item', activeLetter == item ? 'active' : '']" v-for="(item, index) in allLetterList" :key="index" @click.stop="toLetter(item)" @touchend.stop="handleTouchEnd">
{{ item }}
</view>
</scroll-view>
</view>
需要下载js-pinyin包
npm install js-pinyin --save
引入js-pinyin包
import pinyin from 'js-pinyin'
data() {
return {
statusBarHeight: 0,
initiatorInfo: {}, // 发起人
groupNo: '',
allLetterList: ['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', '#'],
memberList: [],
scrollTop: 0,
toView: '',
activeLetter: ''
}
},
computed: {
computedHeight() {
return { height: `calc(100vh - ${this.statusBarHeight}px - 54px )` }
}
},
methods: {
// 获取群组成员
getList() {
getGroupUserList({
groupNo: this.groupNo
}).then(res => {
if (res.code == 'SUCCESS') {
let userList = res.data.userList || []
this.initiatorInfo = res.data.userList[0] || {}
let arr = []
this.allLetterList.forEach((item, index) => {
arr.push({
sign: item,
list: []
})
})
this.allLetterList.forEach((item, index) => {
userList.forEach(item2 => {
let firstLetter = pinyin.getCamelChars(item2.nickName)?.slice(0, 1)
if (firstLetter == item) {
arr[index].list.push(item2)
}
if (this.allLetterList.indexOf(firstLetter) == -1 && item == '#') {
arr[arr.length - 1].list.push(item2)
}
})
})
this.memberList = arr && arr.length ? arr.filter(item => item.list.length > 0) : []
} else {
// 弹出报错提示......
}
})
},
// 滚动
onScroll(e) {
this.scrollTop = e.detail.scrollTop
},
// 滚动到顶部
toTop() {
this.activeLetter = '↑'
this.scrollTop = 0
},
// 滚动到星标好友
toStar() {},
// 滚动到某个字母位置
toLetter(e) {
this.activeLetter = e
if (e == '#') {
this.toView = 'intoView_'
} else {
this.toView = 'intoView' + e
}
},
handleTouchEnd() {
setTimeout(() => {
this.activeLetter = ''
}, 600)
}
}
.main-container {
width: 100%;
height: 100%;
padding: 20rpx 40rpx 0 24rpx;
box-sizing: border-box;
overflow-y: auto;
position: relative;
.member-list {
box-sizing: border-box;
.list-wrap {
margin-top: 30rpx;
.category {
font-size: 24rpx;
font-weight: 500;
color: #adb3ba;
line-height: 32rpx;
margin-bottom: 20rpx;
}
.list-item {
display: flex;
align-items: center;
margin-bottom: 40rpx;
&-img {
width: 70rpx;
height: 70rpx;
background: #d8d8d8;
flex-shrink: 0;
border-radius: 50%;
}
&-name {
flex: 1;
font-size: 28rpx;
font-weight: 500;
color: #2d3f49;
line-height: 36rpx;
padding: 20rpx 16rpx 14rpx;
}
&-btn {
font-size: 24rpx;
font-weight: 500;
color: #ff466d;
line-height: 32rpx;
padding: 14rpx 21rpx;
flex-shrink: 0;
background: #ffedf1;
border-radius: 36rpx;
}
}
.list-item:last-child {
margin-bottom: 0;
}
}
.last-wrap:last-of-type {
padding-bottom: 30rpx;
}
}
.letter-list {
width: 32rpx;
text-align: center;
position: absolute;
top: 50%;
right: 6rpx;
transform: translateY(-50%);
.letter-item {
width: 32rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 22rpx;
font-weight: 500;
color: #999999;
line-height: 32rpx;
}
.active {
width: 32rpx;
height: 32rpx;
background: #fb5c4e;
color: #fff;
border-radius: 50%;
}
}
}