mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-07-12 05:07:31 +08:00
✨ feat: 重构首页Hero、导航菜单与页面布局统一
HomeHero: - 重建每日推荐(左)+私人FM(右)双栏布局 - FM播放/暂停切换、不喜欢/下一首、背景流动动画、均衡器特效 - 修复FM数据获取(res.data.data双层结构) - 歌单预加载改为hover懒加载避免502 导航优化: - SearchBar顶部菜单: 首页/歌单/专辑/排行榜/MV/本地音乐 - 侧边栏隐藏MV和本地音乐(hideInSidebar) - 修复搜索类型切换时失焦收起(@mousedown.prevent) 页面统一: - 新建StickyTabPage通用布局组件(标题+吸顶tabs+内容slot) - 歌单/专辑/MV/播客页面统一使用StickyTabPage重构 - CategorySelector第一项添加ml-0.5防scale裁切 播客优化: - RadioCard简化去除订阅按钮、容忍radio为undefined - 去除最近播放section、loadDashboard包含loadSubscribedRadios i18n: 新碟上架→专辑(5语言)、新增fmTrash/fmNext(5语言)
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
class="py-1.5 px-4 mr-3 inline-block rounded-full cursor-pointer transition-all duration-300 text-sm font-medium bg-gray-100 dark:bg-neutral-800 text-neutral-600 dark:text-neutral-400 hover:bg-gray-200 dark:hover:bg-neutral-700 hover:text-neutral-900 dark:hover:text-white"
|
||||
:class="[
|
||||
animationClass,
|
||||
index === 0 ? 'ml-0.5' : '',
|
||||
isActive(category) ? 'bg-primary text-white shadow-lg shadow-primary/25 scale-105' : ''
|
||||
]"
|
||||
:style="getAnimationDelay(index)"
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="h-full w-full bg-white transition-colors duration-500 dark:bg-black">
|
||||
<n-scrollbar ref="scrollbarRef" class="h-full" :size="100" @scroll="handleScroll">
|
||||
<div class="w-full pb-32">
|
||||
<!-- Page Header (scrolls away) -->
|
||||
<div ref="headerRef" class="page-padding pt-6 pb-2">
|
||||
<h1 class="mb-2 text-2xl font-bold tracking-tight text-neutral-900 md:text-3xl dark:text-white">
|
||||
{{ title }}
|
||||
</h1>
|
||||
<p v-if="description" class="text-neutral-500 dark:text-neutral-400">
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Tabs (sticky on scroll) -->
|
||||
<div
|
||||
class="sticky-tabs z-10 transition-shadow duration-200"
|
||||
:class="isSticky ? 'sticky top-0 shadow-sm' : ''"
|
||||
>
|
||||
<category-selector
|
||||
:model-value="modelValue"
|
||||
:categories="categories"
|
||||
:label-key="labelKey"
|
||||
:value-key="valueKey"
|
||||
@change="(val: any) => emit('change', val)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Content slot -->
|
||||
<div class="page-padding pt-4">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import CategorySelector from '@/components/common/CategorySelector.vue';
|
||||
|
||||
type Category = string | number | { [key: string]: any };
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title: string;
|
||||
description?: string;
|
||||
modelValue: any;
|
||||
categories: Category[];
|
||||
labelKey?: string;
|
||||
valueKey?: string;
|
||||
}>(),
|
||||
{
|
||||
labelKey: 'label',
|
||||
valueKey: 'value'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
change: [value: any];
|
||||
scroll: [e: any];
|
||||
}>();
|
||||
|
||||
const scrollbarRef = ref();
|
||||
const headerRef = ref<HTMLElement>();
|
||||
const isSticky = ref(false);
|
||||
|
||||
const handleScroll = (e: any) => {
|
||||
if (headerRef.value) {
|
||||
const headerBottom = headerRef.value.offsetTop + headerRef.value.offsetHeight;
|
||||
isSticky.value = e.target.scrollTop >= headerBottom;
|
||||
}
|
||||
emit('scroll', e);
|
||||
};
|
||||
|
||||
const scrollTo = (options: ScrollToOptions) => {
|
||||
scrollbarRef.value?.scrollTo(options);
|
||||
};
|
||||
|
||||
defineExpose({ scrollbarRef, scrollTo });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sticky-tabs {
|
||||
background: inherit;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user