2025-03-29 16:01:43 +08:00
|
|
|
<template>
|
2025-04-07 10:21:22 +08:00
|
|
|
<view class="page">
|
|
|
|
<!-- 加载状态 -->
|
|
|
|
<view v-if="loading" class="loading-state">
|
|
|
|
<view class="loading-skeleton" v-for="i in 5" :key="i">
|
|
|
|
<view class="skeleton-logo"></view>
|
|
|
|
<view class="skeleton-content">
|
|
|
|
<view class="skeleton-title"></view>
|
|
|
|
<view class="skeleton-subtitle"></view>
|
2025-03-29 16:01:43 +08:00
|
|
|
</view>
|
|
|
|
</view>
|
2025-04-07 10:21:22 +08:00
|
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 字母索引列表 -->
|
|
|
|
<view v-else class="index-list">
|
|
|
|
<scroll-view
|
|
|
|
scroll-y
|
|
|
|
class="content"
|
|
|
|
:scroll-into-view="currentLetter ? 'letter-' + currentLetter : ''"
|
2025-04-08 01:07:18 +08:00
|
|
|
@scroll="onScroll"
|
|
|
|
:scroll-with-animation="true"
|
2025-04-07 10:21:22 +08:00
|
|
|
>
|
|
|
|
<block v-for="(group, letter) in groupedBreweries" :key="letter">
|
2025-04-08 01:07:18 +08:00
|
|
|
<view :id="'letter-' + letter" class="letter-section" :data-letter="letter">
|
2025-04-07 10:21:22 +08:00
|
|
|
<view class="letter-title">{{letter}}</view>
|
|
|
|
<view
|
|
|
|
class="brewery-item hover-effect"
|
|
|
|
v-for="brewery in group"
|
|
|
|
:key="brewery.id"
|
|
|
|
@click="navigateToBrewery(brewery)"
|
|
|
|
>
|
|
|
|
<image
|
2025-04-08 01:07:18 +08:00
|
|
|
:src="brewery.brandLogo || '/static/images/default-logo.png'"
|
2025-04-07 10:21:22 +08:00
|
|
|
class="brewery-logo"
|
|
|
|
mode="aspectFill"
|
|
|
|
:lazy-load="true"
|
|
|
|
/>
|
|
|
|
<view class="brewery-info">
|
2025-04-08 01:07:18 +08:00
|
|
|
<text class="brewery-name text-ellipsis">{{brewery.brandName}}</text>
|
2025-04-07 10:21:22 +08:00
|
|
|
<text class="beer-count">{{brewery.beerCount || 0}}款在售</text>
|
2025-03-29 16:01:43 +08:00
|
|
|
</view>
|
2025-04-07 10:21:22 +08:00
|
|
|
<image
|
|
|
|
src="/static/icons/arrow-right.png"
|
|
|
|
class="arrow-icon"
|
|
|
|
/>
|
2025-03-29 16:01:43 +08:00
|
|
|
</view>
|
|
|
|
</view>
|
2025-04-07 10:21:22 +08:00
|
|
|
</block>
|
|
|
|
|
|
|
|
<!-- 空状态 -->
|
|
|
|
<view v-if="Object.keys(groupedBreweries).length === 0" class="empty-state">
|
|
|
|
<image src="/static/images/empty.png" class="empty-image" />
|
|
|
|
<text class="empty-text">暂无相关品牌</text>
|
2025-03-29 16:01:43 +08:00
|
|
|
</view>
|
|
|
|
</scroll-view>
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
<!-- 右侧字母导航 -->
|
|
|
|
<view class="letter-nav">
|
2025-04-08 01:07:18 +08:00
|
|
|
<view class="indicator" :style="indicatorStyle"></view>
|
2025-04-07 10:21:22 +08:00
|
|
|
<view
|
|
|
|
v-for="letter in letters"
|
|
|
|
:key="letter"
|
|
|
|
class="letter-item"
|
|
|
|
:class="{ active: currentLetter === letter }"
|
|
|
|
@click="scrollToLetter(letter)"
|
|
|
|
>
|
|
|
|
{{letter}}
|
2025-03-29 16:01:43 +08:00
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</view>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2025-04-07 10:21:22 +08:00
|
|
|
import { getBreweries, getBeerList } from '@/api/bar.js'
|
2025-04-08 01:07:18 +08:00
|
|
|
import Pinyin from '@/utils/js-pinyin.js'
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'HotLabel',
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
// 数据相关
|
|
|
|
breweries: [],
|
|
|
|
groupedBreweries: {},
|
|
|
|
currentLetter: '',
|
|
|
|
letters: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
|
|
|
|
|
|
|
|
// 加载状态
|
2025-04-08 01:07:18 +08:00
|
|
|
loading: false,
|
|
|
|
scrollTimer: null,
|
|
|
|
indicatorTop: 0,
|
|
|
|
observer: null,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
indicatorStyle() {
|
|
|
|
return {
|
|
|
|
transform: `translateY(${this.indicatorTop}px)`,
|
|
|
|
}
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
2025-03-29 16:01:43 +08:00
|
|
|
},
|
2025-04-08 01:07:18 +08:00
|
|
|
created() {
|
|
|
|
Pinyin.setOptions({ checkPolyphone: false, charCase: 1 })
|
|
|
|
},
|
2025-04-07 10:21:22 +08:00
|
|
|
onShow() {
|
|
|
|
this.initData()
|
2025-03-29 16:01:43 +08:00
|
|
|
},
|
2025-04-08 01:07:18 +08:00
|
|
|
mounted() {
|
|
|
|
this.setupIntersectionObserver()
|
|
|
|
},
|
|
|
|
beforeDestroy() {
|
|
|
|
if (this.observer) {
|
|
|
|
this.observer.disconnect()
|
|
|
|
}
|
|
|
|
},
|
2025-04-07 10:21:22 +08:00
|
|
|
methods: {
|
|
|
|
// 初始化数据
|
|
|
|
async initData() {
|
|
|
|
try {
|
|
|
|
uni.showLoading({
|
|
|
|
title: '加载中...'
|
|
|
|
})
|
|
|
|
|
2025-04-08 01:07:18 +08:00
|
|
|
const res = await getBreweries()
|
|
|
|
if (res && res.code === 200 && res.rows) {
|
|
|
|
// 直接使用后端返回的原始字段
|
|
|
|
this.breweries = res.rows.map(item => ({
|
|
|
|
...item, // 保留所有原始字段
|
|
|
|
beerCount: 0, // 仅添加前端所需的计数字段
|
2025-04-07 10:21:22 +08:00
|
|
|
}))
|
|
|
|
|
|
|
|
// 获取每个品牌的酒款数量
|
|
|
|
await this.getBeerCounts()
|
|
|
|
// 按首字母分组
|
|
|
|
this.groupBreweries()
|
|
|
|
} else {
|
2025-04-08 01:07:18 +08:00
|
|
|
throw new Error(res?.msg || '获取品牌列表失败')
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error('初始化数据失败:', error)
|
|
|
|
uni.showToast({
|
2025-04-08 01:07:18 +08:00
|
|
|
title: error.message || '加载失败,请重试',
|
2025-04-07 10:21:22 +08:00
|
|
|
icon: 'none'
|
|
|
|
})
|
|
|
|
} finally {
|
|
|
|
uni.hideLoading()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// 获取每个品牌的酒款数量
|
|
|
|
async getBeerCounts() {
|
|
|
|
const promises = this.breweries.map(async (brewery) => {
|
|
|
|
try {
|
|
|
|
const res = await getBeerList({ breweryId: brewery.id })
|
|
|
|
if (res && res.data) {
|
|
|
|
brewery.beerCount = res.data.total || 0
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`获取品牌 ${brewery.id} 的酒款数量失败:`, error)
|
|
|
|
brewery.beerCount = 0
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
await Promise.all(promises)
|
|
|
|
},
|
|
|
|
|
|
|
|
// 按首字母分组品牌
|
|
|
|
groupBreweries() {
|
|
|
|
const grouped = {}
|
|
|
|
|
|
|
|
this.breweries.forEach(brewery => {
|
2025-04-08 01:07:18 +08:00
|
|
|
if (!brewery.brandName) return
|
2025-04-07 10:21:22 +08:00
|
|
|
|
2025-04-08 01:07:18 +08:00
|
|
|
let firstLetter = brewery.brandName.charAt(0).toUpperCase()
|
|
|
|
if (/[\u4e00-\u9fa5]/.test(firstLetter)) {
|
|
|
|
firstLetter = this.getFirstPinYinLetter(brewery.brandName)
|
|
|
|
}
|
2025-04-07 10:21:22 +08:00
|
|
|
if (!/[A-Z]/.test(firstLetter)) {
|
|
|
|
firstLetter = '#'
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!grouped[firstLetter]) {
|
|
|
|
grouped[firstLetter] = []
|
|
|
|
}
|
|
|
|
grouped[firstLetter].push(brewery)
|
|
|
|
})
|
|
|
|
|
|
|
|
Object.keys(grouped).forEach(letter => {
|
|
|
|
grouped[letter].sort((a, b) =>
|
2025-04-08 01:07:18 +08:00
|
|
|
a.brandName.localeCompare(b.brandName, 'zh')
|
2025-04-07 10:21:22 +08:00
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
this.letters = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')]
|
|
|
|
if (grouped['#']) {
|
|
|
|
this.letters.push('#')
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('分组后的数据:', grouped)
|
|
|
|
this.groupedBreweries = grouped
|
|
|
|
},
|
|
|
|
|
2025-04-08 01:07:18 +08:00
|
|
|
// 设置交叉观察器
|
|
|
|
setupIntersectionObserver() {
|
|
|
|
if (typeof uni.createIntersectionObserver !== 'function') return
|
2025-04-07 10:21:22 +08:00
|
|
|
|
2025-04-08 01:07:18 +08:00
|
|
|
this.observer = uni.createIntersectionObserver(this)
|
|
|
|
this.observer
|
|
|
|
.relativeTo('.content')
|
|
|
|
.observe('.letter-section', (res) => {
|
|
|
|
if (res.intersectionRatio > 0) {
|
|
|
|
const letter = res.dataset.letter
|
|
|
|
this.updateCurrentLetter(letter, false)
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
2025-04-08 01:07:18 +08:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
// 滚动事件处理
|
|
|
|
onScroll(e) {
|
|
|
|
if (this.scrollTimer) {
|
|
|
|
clearTimeout(this.scrollTimer)
|
|
|
|
}
|
|
|
|
|
|
|
|
this.scrollTimer = setTimeout(() => {
|
|
|
|
const query = uni.createSelectorQuery().in(this)
|
|
|
|
query.selectAll('.letter-section').boundingClientRect()
|
|
|
|
query.select('.content').boundingClientRect()
|
|
|
|
query.exec(([sections, content]) => {
|
|
|
|
if (!sections || !content) return
|
|
|
|
|
|
|
|
const contentTop = content.top
|
|
|
|
let currentSection = null
|
|
|
|
let minDistance = Infinity
|
|
|
|
|
|
|
|
// 找到距离顶部最近的section
|
|
|
|
sections.forEach(section => {
|
|
|
|
const distance = Math.abs(section.top - contentTop)
|
|
|
|
if (distance < minDistance) {
|
|
|
|
minDistance = distance
|
|
|
|
currentSection = section
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if (currentSection) {
|
|
|
|
const letter = currentSection.dataset.letter
|
|
|
|
this.updateCurrentLetter(letter, false)
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
})
|
2025-04-08 01:07:18 +08:00
|
|
|
}, 50) // 降低延迟以提高响应速度
|
|
|
|
},
|
|
|
|
|
|
|
|
// 更新当前字母
|
|
|
|
updateCurrentLetter(letter, shouldScroll = true) {
|
|
|
|
if (this.currentLetter === letter) return
|
|
|
|
|
|
|
|
this.currentLetter = letter
|
|
|
|
|
|
|
|
// 更新指示器位置
|
|
|
|
const query = uni.createSelectorQuery().in(this)
|
|
|
|
query.selectAll('.letter-item').boundingClientRect()
|
|
|
|
query.select('.letter-nav').boundingClientRect()
|
|
|
|
query.exec(([items, nav]) => {
|
|
|
|
if (!items || !nav) return
|
2025-04-07 10:21:22 +08:00
|
|
|
|
2025-04-08 01:07:18 +08:00
|
|
|
const index = this.letters.indexOf(letter)
|
|
|
|
if (index > -1) {
|
|
|
|
const item = items[index]
|
|
|
|
// 调整指示器位置,确保准确对齐
|
|
|
|
this.indicatorTop = item.top - nav.top + (item.height - 48) / 2 // 48是指示器的高度
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// 是否需要滚动到对应位置
|
|
|
|
if (shouldScroll) {
|
|
|
|
uni.pageScrollTo({
|
|
|
|
selector: `#letter-${letter}`,
|
|
|
|
duration: 300
|
|
|
|
})
|
|
|
|
}
|
2025-04-07 10:21:22 +08:00
|
|
|
},
|
2025-04-08 01:07:18 +08:00
|
|
|
|
2025-04-07 10:21:22 +08:00
|
|
|
// 滚动到指定字母
|
|
|
|
scrollToLetter(letter) {
|
2025-04-08 01:07:18 +08:00
|
|
|
this.updateCurrentLetter(letter)
|
|
|
|
// 已有的滚动逻辑保持不变
|
2025-04-07 10:21:22 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
// 导航到品牌详情
|
|
|
|
navigateToBrewery(brewery) {
|
|
|
|
uni.navigateTo({
|
|
|
|
url: `/pages/activityList/styleSelection?breweryId=${brewery.id}`
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
// 获取中文首字母
|
|
|
|
getFirstPinYinLetter(str) {
|
|
|
|
if (!str) return '#'
|
|
|
|
const firstChar = str.charAt(0)
|
|
|
|
if (!/[\u4e00-\u9fa5]/.test(firstChar)) return firstChar.toUpperCase()
|
2025-04-08 01:07:18 +08:00
|
|
|
|
|
|
|
// 使用js-pinyin获取拼音首字母
|
|
|
|
const pinyin = Pinyin.getFullChars(firstChar)
|
|
|
|
return pinyin ? pinyin.charAt(0).toUpperCase() : '#'
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-03-29 16:01:43 +08:00
|
|
|
</script>
|
|
|
|
|
2025-04-07 10:21:22 +08:00
|
|
|
<style lang="scss" scoped>
|
2025-03-29 16:01:43 +08:00
|
|
|
.page {
|
2025-04-07 10:21:22 +08:00
|
|
|
min-height: 100vh;
|
2025-04-08 01:07:18 +08:00
|
|
|
background: #F8F9FC;
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
// 加载状态
|
|
|
|
.loading-state {
|
|
|
|
padding: 24rpx;
|
|
|
|
|
|
|
|
.loading-skeleton {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
2025-04-08 01:07:18 +08:00
|
|
|
padding: 32rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
margin-bottom: 24rpx;
|
|
|
|
background: #FFFFFF;
|
2025-04-08 01:07:18 +08:00
|
|
|
border-radius: 16rpx;
|
|
|
|
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
.skeleton-logo {
|
2025-04-08 01:07:18 +08:00
|
|
|
width: 88rpx;
|
|
|
|
height: 88rpx;
|
|
|
|
background: linear-gradient(90deg, #F5F5F5, #FAFAFA, #F5F5F5);
|
|
|
|
border-radius: 16rpx;
|
|
|
|
margin-right: 32rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
animation: skeleton-loading 1.5s infinite;
|
|
|
|
}
|
|
|
|
|
|
|
|
.skeleton-content {
|
|
|
|
flex: 1;
|
|
|
|
|
|
|
|
.skeleton-title {
|
|
|
|
width: 60%;
|
2025-04-08 01:07:18 +08:00
|
|
|
height: 36rpx;
|
|
|
|
background: linear-gradient(90deg, #F5F5F5, #FAFAFA, #F5F5F5);
|
|
|
|
border-radius: 8rpx;
|
|
|
|
margin-bottom: 16rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
animation: skeleton-loading 1.5s infinite;
|
|
|
|
}
|
|
|
|
|
|
|
|
.skeleton-subtitle {
|
|
|
|
width: 40%;
|
2025-04-08 01:07:18 +08:00
|
|
|
height: 28rpx;
|
|
|
|
background: linear-gradient(90deg, #F5F5F5, #FAFAFA, #F5F5F5);
|
|
|
|
border-radius: 6rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
animation: skeleton-loading 1.5s infinite;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 字母索引列表
|
|
|
|
.index-list {
|
|
|
|
display: flex;
|
2025-04-08 01:07:18 +08:00
|
|
|
height: 100vh;
|
|
|
|
background: #FFFFFF;
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
.content {
|
|
|
|
flex: 1;
|
|
|
|
height: 100%;
|
|
|
|
|
|
|
|
.letter-section {
|
|
|
|
.letter-title {
|
2025-04-08 01:07:18 +08:00
|
|
|
padding: 20rpx 32rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
font-size: 28rpx;
|
2025-04-08 01:07:18 +08:00
|
|
|
color: #666666;
|
|
|
|
background: #F8F9FC;
|
|
|
|
font-weight: 600;
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
.brewery-item {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
2025-04-08 01:07:18 +08:00
|
|
|
padding: 32rpx;
|
|
|
|
margin: 0 32rpx;
|
|
|
|
border-bottom: 2rpx solid #F5F7FA;
|
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
position: relative;
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
&:active {
|
2025-04-08 01:07:18 +08:00
|
|
|
background: #F8F9FC;
|
|
|
|
transform: scale(0.99);
|
|
|
|
}
|
|
|
|
|
|
|
|
&::after {
|
|
|
|
content: '';
|
|
|
|
position: absolute;
|
|
|
|
left: 32rpx;
|
|
|
|
right: 32rpx;
|
|
|
|
bottom: 0;
|
|
|
|
height: 2rpx;
|
|
|
|
background: #F5F7FA;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:last-child::after {
|
|
|
|
display: none;
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
.brewery-logo {
|
2025-04-08 01:07:18 +08:00
|
|
|
width: 88rpx;
|
|
|
|
height: 88rpx;
|
|
|
|
border-radius: 16rpx;
|
|
|
|
margin-right: 32rpx;
|
|
|
|
background: #F8F9FC;
|
|
|
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.06);
|
|
|
|
transition: transform 0.3s ease;
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
transform: scale(0.95);
|
|
|
|
}
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
.brewery-info {
|
|
|
|
flex: 1;
|
|
|
|
|
|
|
|
.brewery-name {
|
2025-04-08 01:07:18 +08:00
|
|
|
font-size: 30rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
color: #333333;
|
2025-04-08 01:07:18 +08:00
|
|
|
margin-bottom: 12rpx;
|
|
|
|
font-weight: 600;
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
.beer-count {
|
2025-04-08 01:07:18 +08:00
|
|
|
font-size: 26rpx;
|
|
|
|
color: #666666;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
&::before {
|
|
|
|
content: '';
|
|
|
|
display: inline-block;
|
|
|
|
width: 12rpx;
|
|
|
|
height: 12rpx;
|
|
|
|
border-radius: 50%;
|
|
|
|
background: #19C375;
|
|
|
|
margin-right: 8rpx;
|
|
|
|
}
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.arrow-icon {
|
2025-04-08 01:07:18 +08:00
|
|
|
width: 36rpx;
|
|
|
|
height: 36rpx;
|
|
|
|
opacity: 0.4;
|
|
|
|
margin-left: 24rpx;
|
|
|
|
transition: transform 0.3s ease;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:active .arrow-icon {
|
|
|
|
transform: translateX(4rpx);
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.empty-state {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: center;
|
2025-04-08 01:07:18 +08:00
|
|
|
padding: 120rpx 0;
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
.empty-image {
|
2025-04-08 01:07:18 +08:00
|
|
|
width: 280rpx;
|
|
|
|
height: 280rpx;
|
|
|
|
margin-bottom: 40rpx;
|
|
|
|
opacity: 0.8;
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
.empty-text {
|
2025-04-08 01:07:18 +08:00
|
|
|
font-size: 30rpx;
|
|
|
|
color: #666666;
|
|
|
|
letter-spacing: 2rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 右侧字母导航
|
|
|
|
.letter-nav {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
justify-content: center;
|
2025-04-08 01:07:18 +08:00
|
|
|
padding: 24rpx 16rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
background: #FFFFFF;
|
2025-04-08 01:07:18 +08:00
|
|
|
box-shadow: -4rpx 0 16rpx rgba(0,0,0,0.03);
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
.indicator {
|
|
|
|
position: absolute;
|
|
|
|
left: 50%;
|
|
|
|
width: 48rpx;
|
|
|
|
height: 48rpx;
|
|
|
|
border-radius: 50%;
|
|
|
|
background: rgba(25, 54, 122, 0.1);
|
|
|
|
transform: translateX(-50%);
|
|
|
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
pointer-events: none;
|
|
|
|
z-index: 1;
|
|
|
|
}
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
.letter-item {
|
2025-04-08 01:07:18 +08:00
|
|
|
width: 40rpx;
|
|
|
|
height: 40rpx;
|
2025-04-07 10:21:22 +08:00
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
font-size: 24rpx;
|
|
|
|
color: #666666;
|
|
|
|
margin: 4rpx 0;
|
|
|
|
border-radius: 50%;
|
2025-04-08 01:07:18 +08:00
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
font-weight: 500;
|
|
|
|
position: relative;
|
|
|
|
z-index: 2;
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
&.active {
|
|
|
|
color: #FFFFFF;
|
2025-04-08 01:07:18 +08:00
|
|
|
background: #19367A;
|
|
|
|
font-weight: 600;
|
|
|
|
transform: scale(1.1);
|
|
|
|
box-shadow: 0 4rpx 8rpx rgba(25, 54, 122, 0.2);
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
transform: scale(0.9);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-03-29 16:01:43 +08:00
|
|
|
}
|
|
|
|
|
2025-04-07 10:21:22 +08:00
|
|
|
@keyframes skeleton-loading {
|
|
|
|
0% {
|
2025-04-08 01:07:18 +08:00
|
|
|
background-position: -200% 0;
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
|
|
|
100% {
|
2025-04-08 01:07:18 +08:00
|
|
|
background-position: 200% 0;
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
2025-03-29 16:01:43 +08:00
|
|
|
}
|
|
|
|
|
2025-04-07 10:21:22 +08:00
|
|
|
// 添加按键反馈效果
|
|
|
|
.hover-effect {
|
2025-04-08 01:07:18 +08:00
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
2025-04-07 10:21:22 +08:00
|
|
|
|
|
|
|
&:active {
|
2025-04-08 01:07:18 +08:00
|
|
|
transform: scale(0.98);
|
|
|
|
opacity: 0.9;
|
2025-04-07 10:21:22 +08:00
|
|
|
}
|
2025-03-29 16:01:43 +08:00
|
|
|
}
|
|
|
|
|
2025-04-07 10:21:22 +08:00
|
|
|
// 文本省略
|
|
|
|
.text-ellipsis {
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
white-space: nowrap;
|
2025-03-29 16:01:43 +08:00
|
|
|
}
|
|
|
|
</style>
|