<template>
? <view class="container">
? ? <!-- 左側地區列表 -->
? ? <scroll-view
? ? ? class="left-list"
? ? ? scroll-y
? ? ? :scroll-into-view="currentLetterId"
? ? ? scroll-with-animation
? ? ? @scroll="onScroll"
? ? ? ref="scrollView"
? ? >
? ? ? <view
? ? ? ? v-for="group in dataList"
? ? ? ? :key="group.letter"
? ? ? ? :id="'letter-' + group.letter"
? ? ? ? class="group"
? ? ? ? :data-letter="group.letter"
? ? ? >
? ? ? ? <view class="group-letter">{{ group.letter }}</view>
? ? ? ? <view style="display: flex; gap: 20rpx; padding: 20rpx;">
?? ??? ??? ??? ??? ?<view v-for="item in group.items" :key="item" class="item">{{ item }}</view>
?? ??? ??? ??? ?</view>
? ? ? </view>
? ? </scroll-view>
? ? <!-- 右側字母索引 -->
? ? <view
? ? ? class="right-index"
? ? ? @touchstart.prevent="onTouchStart"
? ? ? @touchmove.prevent="onTouchMove"
? ? ? @touchend.prevent="onTouchEnd"
? ? ? ref="indexWrapper"
? ? >
? ? ? <view
? ? ? ? v-for="(letter, index) in letters"
? ? ? ? :key="letter"
? ? ? ? class="letter"
? ? ? ? :class="{ active: letter === activeLetter }"
? ? ? ? :data-index="index"
? ? ? >
? ? ? ? {{ letter }}
? ? ? </view>
? ? </view>
? </view>
</template>
<script>
export default {
? data() {
? ? return {
? ? ? letters: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
? ? ? dataList: [
? ? ? ? { letter: 'A', items: ['安慶', '鞍山', '安陽'] },
? ? ? ? { letter: 'B', items: ['北京', '保定', '包頭'] },
? ? ? ? { letter: 'C', items: ['長沙', '成都', '重慶'] },
? ? ? ? { letter: 'D', items: ['大連', '東莞', '德陽'] },
? ? ? ? { letter: 'F', items: ['福州', '佛山'] },
? ? ? ? { letter: 'G', items: ['廣州', '貴陽'] },
? ? ? ? { letter: 'H', items: ['杭州', '合肥', '哈爾濱'] },
? ? ? ? { letter: 'J', items: ['濟南', '嘉興'] },
? ? ? ? { letter: 'K', items: ['昆明'] },
? ? ? ? { letter: 'L', items: ['蘭州', '洛陽'] },
? ? ? ? { letter: 'M', items: ['綿陽'] },
? ? ? ? { letter: 'N', items: ['南京', '南昌'] },
? ? ? ? { letter: 'Q', items: ['青島', '泉州'] },
? ? ? ? { letter: 'S', items: ['上海', '蘇州', '深圳'] },
? ? ? ? { letter: 'T', items: ['天津', '唐山'] },
? ? ? ? { letter: 'W', items: ['武漢', '無錫'] },
? ? ? ? { letter: 'X', items: ['廈門', '西安'] },
? ? ? ? { letter: 'Y', items: ['煙臺', '宜昌'] },
? ? ? ? { letter: 'Z', items: ['鄭州', '珠海'] }
? ? ? ],
? ? ? currentLetterId: '',
? ? ? activeLetter: '',
? ? ? indexItemHeight: 0,
? ? ? indexTop: 0,
? ? };
? },
? mounted() {
? ? // 獲取右側字母索引容器的位置和單個字母高度
? ? uni.createSelectorQuery()
? ? ? .select('.right-index')
? ? ? .boundingClientRect(rect => {
?? ??? ??? ??? ?console.log('打印高度')
?? ??? ??? ??? ?console.log(rect)
? ? ? ? this.indexTop = rect.top;
? ? ? })
? ? ? .exec();
?? ??? ??? ?// 獲取每個字母的高度
?? ??? ??? ?this.$nextTick(() => {
?? ??? ??? ??? ?const letterHeights = [];
?? ??? ??? ??? ?this.letters.forEach((letter, index) => {
?? ??? ??? ??? ??? ?uni.createSelectorQuery()
?? ??? ??? ??? ??? ??? ?.select(`.right-index .letter:nth-child(${index + 1})`)
?? ??? ??? ??? ??? ??? ?.boundingClientRect(rect => {
?? ??? ??? ??? ??? ??? ??? ?letterHeights.push(rect.height);
?? ??? ??? ??? ??? ??? ??? ?// 當所有字母的高度都獲取到后,計算平均高度
?? ??? ??? ??? ??? ??? ??? ?if (letterHeights.length === this.letters.length) {
?? ??? ??? ??? ??? ??? ??? ??? ?this.indexItemHeight = letterHeights.reduce((a, b) => a + b, 0) / letterHeights.length;
?? ??? ??? ??? ??? ??? ??? ??? ?console.log('字母高度')
?? ??? ??? ??? ??? ??? ??? ??? ?console.log(this.indexItemHeight)
?? ??? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ?})
?? ??? ??? ??? ??? ??? ?.exec();
?? ??? ??? ??? ?});
?? ??? ??? ?});
? },
? methods: {
? ? scrollToLetter(letter) {
? ? ? this.currentLetterId = 'letter-' + letter;
? ? ? this.activeLetter = letter;
? ? },
? ? onTouchStart(e) {
? ? ? this.handleTouch(e);
? ? },
? ? onTouchMove(e) {
? ? ? this.handleTouch(e);
? ? },
? ? onTouchEnd() {
? ? ? // 觸摸結束可以做一些清理操作,如果需要
? ? },
? ? handleTouch(e) {
? ? ? const touch = e.touches[0];
? ? ? const y = touch.clientY;
? ? ? // 計算觸摸點相對于字母索引頂部的偏移
? ? ? let index = Math.floor((y - this.indexTop) / this.indexItemHeight);
? ? ? if (index < 0) index = 0;
? ? ? if (index >= this.letters.length) index = this.letters.length - 1;
? ? ? const letter = this.letters[index];
? ? ? if (letter && letter !== this.activeLetter) {
? ? ? ? this.scrollToLetter(letter);
? ? ? }
? ? },
? ? onScroll(e) {
? ? ? // 可選:根據滾動位置動態改變右側高亮字母
? ? ? // 這里可以實現滾動聯動右側字母高亮,稍復雜,可根據需求添加
? ? }
? }
};
</script>
<style>
.container {
? display: flex;
? height: 100vh;
?? ?background-color: #fff;
}
.left-list {
? flex: 1;
}
.group {
? padding: 20rpx 0;
}
.group-letter {
? font-weight: bold;
? font-size: 36rpx;
? background: #F2F4F5;
? padding: 10rpx 30rpx;
}
.item {
?? ?width: 120rpx;
?? ?height: 42rpx;
?? ?line-height: 42rpx;
?? ?background-color: #F2F4F5;
?? ?text-align: center;
?? ?border-radius: 30rpx;
?? ?font-size: 24rpx;
}
.right-index {
? width: 60rpx;
? background: transparent;
? display: flex;
? flex-direction: column;
? align-items: center;
? justify-content: flex-start;
? user-select: none;
? /* 方便觸摸 */
? padding: 10rpx 0;
}
.letter {
? font-size: 28rpx;
? color: #666;
? padding: 4rpx 0;
? width: 100%;
? text-align: center;
? cursor: pointer;
}
.letter.active {
? color: #007AFF;
? font-weight: bold;
}
/* 通用隱藏方案 */
::-webkit-scrollbar {
? display: none !important;
? width: 0 !important;
? height: 0 !important;
? -webkit-appearance: none;
? background: transparent;
}
/* 安卓專用優化 */
/* #ifdef APP-PLUS */
scroll-view ::-webkit-scrollbar {
? display: none;
}
/* #endif */
</style>
最后效果,可以根據需要調試