mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-24 16:27:23 +08:00
feat: 优化移动端适配
This commit is contained in:
@@ -38,6 +38,7 @@
|
|||||||
:title="album.name"
|
:title="album.name"
|
||||||
:subtitle="getArtistNames(album)"
|
:subtitle="getArtistNames(album)"
|
||||||
:tracks="albumTracksMap[album.id] || []"
|
:tracks="albumTracksMap[album.id] || []"
|
||||||
|
:show-hover-tracks="!isMobile"
|
||||||
:animation-delay="calculateAnimationDelay(index, 0.04)"
|
:animation-delay="calculateAnimationDelay(index, 0.04)"
|
||||||
@click="handleAlbumClick(album)"
|
@click="handleAlbumClick(album)"
|
||||||
@play="playAlbum(album)"
|
@play="playAlbum(album)"
|
||||||
@@ -62,7 +63,7 @@ import { getAlbum } from '@/api/list';
|
|||||||
import { navigateToMusicList } from '@/components/common/MusicListNavigator';
|
import { navigateToMusicList } from '@/components/common/MusicListNavigator';
|
||||||
import { usePlayerCoreStore } from '@/store/modules/playerCore';
|
import { usePlayerCoreStore } from '@/store/modules/playerCore';
|
||||||
import { usePlaylistStore } from '@/store/modules/playlist';
|
import { usePlaylistStore } from '@/store/modules/playlist';
|
||||||
import { calculateAnimationDelay, isElectron } from '@/utils';
|
import { calculateAnimationDelay, isElectron, isMobile } from '@/utils';
|
||||||
|
|
||||||
import HomeListItem from './HomeListItem.vue';
|
import HomeListItem from './HomeListItem.vue';
|
||||||
|
|
||||||
@@ -104,7 +105,7 @@ const fetchAlbums = async () => {
|
|||||||
if (data.code === 200) {
|
if (data.code === 200) {
|
||||||
albums.value = data.weekData || data.monthData || data.albums || [];
|
albums.value = data.weekData || data.monthData || data.albums || [];
|
||||||
// Preload tracks for displayed albums (Electron only)
|
// Preload tracks for displayed albums (Electron only)
|
||||||
if (isElectron) {
|
if (isElectron && !isMobile.value) {
|
||||||
preloadAllTracks();
|
preloadAllTracks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ import { onMounted, ref } from 'vue';
|
|||||||
|
|
||||||
import { getHotSinger } from '@/api/home';
|
import { getHotSinger } from '@/api/home';
|
||||||
import { useArtist } from '@/hooks/useArtist';
|
import { useArtist } from '@/hooks/useArtist';
|
||||||
import { calculateAnimationDelay, getImgUrl } from '@/utils';
|
import { calculateAnimationDelay, getImgUrl, isMobile } from '@/utils';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title: string;
|
title: string;
|
||||||
@@ -100,6 +100,7 @@ const fetchArtists = async () => {
|
|||||||
|
|
||||||
// Enhanced horizontal scroll with wheel support
|
// Enhanced horizontal scroll with wheel support
|
||||||
const handleWheel = (e: WheelEvent) => {
|
const handleWheel = (e: WheelEvent) => {
|
||||||
|
if (isMobile.value) return;
|
||||||
if (!scrollContainer.value) return;
|
if (!scrollContainer.value) return;
|
||||||
|
|
||||||
// Prevent default vertical scroll
|
// Prevent default vertical scroll
|
||||||
@@ -161,7 +162,7 @@ onMounted(() => {
|
|||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
/* Optimize for touch */
|
/* Optimize for touch */
|
||||||
touch-action: pan-x;
|
touch-action: pan-x pan-y;
|
||||||
}
|
}
|
||||||
|
|
||||||
.artists-track {
|
.artists-track {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
<!-- Hover Overlay with Song Preview -->
|
<!-- Hover Overlay with Song Preview -->
|
||||||
<div
|
<div
|
||||||
|
v-if="showHoverTracks"
|
||||||
class="absolute inset-0 flex items-end opacity-0 transition-all duration-500 ease-out group-hover:opacity-100"
|
class="absolute inset-0 flex items-end opacity-0 transition-all duration-500 ease-out group-hover:opacity-100"
|
||||||
:style="overlayStyle"
|
:style="overlayStyle"
|
||||||
>
|
>
|
||||||
@@ -108,10 +109,12 @@ const props = withDefaults(
|
|||||||
badgeType?: 'new' | 'hot' | 'recommend';
|
badgeType?: 'new' | 'hot' | 'recommend';
|
||||||
playCount?: number;
|
playCount?: number;
|
||||||
animationDelay?: string;
|
animationDelay?: string;
|
||||||
|
showHoverTracks?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
tracks: () => [],
|
tracks: () => [],
|
||||||
animationDelay: '0s'
|
animationDelay: '0s',
|
||||||
|
showHoverTracks: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading Skeleton -->
|
<!-- Loading Skeleton -->
|
||||||
<div
|
<div v-if="loading" class="songs-grid grid gap-3" :class="gridClass">
|
||||||
v-if="loading"
|
|
||||||
class="songs-grid grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
v-for="i in 10"
|
v-for="i in 10"
|
||||||
:key="i"
|
:key="i"
|
||||||
@@ -30,10 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Songs Grid (Even columns: 1→2→3→4→5) -->
|
<!-- Songs Grid (Even columns: 1→2→3→4→5) -->
|
||||||
<div
|
<div v-else class="songs-grid grid gap-2 md:gap-3" :class="gridClass">
|
||||||
v-else
|
|
||||||
class="songs-grid grid grid-cols-1 gap-2 md:gap-3 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5"
|
|
||||||
>
|
|
||||||
<song-item
|
<song-item
|
||||||
v-for="(song, index) in songs"
|
v-for="(song, index) in songs"
|
||||||
:key="song.id"
|
:key="song.id"
|
||||||
@@ -49,14 +43,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import { getRecommendMusic } from '@/api/home';
|
import { getRecommendMusic } from '@/api/home';
|
||||||
import SongItem from '@/components/common/SongItem.vue';
|
import SongItem from '@/components/common/SongItem.vue';
|
||||||
import { usePlayerStore } from '@/store';
|
import { usePlayerStore } from '@/store';
|
||||||
import { SongResult } from '@/types/music';
|
import { SongResult } from '@/types/music';
|
||||||
import { calculateAnimationDelay } from '@/utils';
|
import { calculateAnimationDelay, isMobile } from '@/utils';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title: string;
|
title: string;
|
||||||
@@ -67,6 +61,11 @@ const { t } = useI18n();
|
|||||||
const playerStore = usePlayerStore();
|
const playerStore = usePlayerStore();
|
||||||
const songs = ref<SongResult[]>([]);
|
const songs = ref<SongResult[]>([]);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
|
const gridClass = computed(() =>
|
||||||
|
isMobile.value
|
||||||
|
? 'grid-cols-1 sm:grid-cols-2'
|
||||||
|
: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5'
|
||||||
|
);
|
||||||
|
|
||||||
const fetchSongs = async () => {
|
const fetchSongs = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ import { getListDetail } from '@/api/list';
|
|||||||
import { navigateToMusicList } from '@/components/common/MusicListNavigator';
|
import { navigateToMusicList } from '@/components/common/MusicListNavigator';
|
||||||
import { usePlayerCoreStore } from '@/store/modules/playerCore';
|
import { usePlayerCoreStore } from '@/store/modules/playerCore';
|
||||||
import { usePlaylistStore } from '@/store/modules/playlist';
|
import { usePlaylistStore } from '@/store/modules/playlist';
|
||||||
import { calculateAnimationDelay, isElectron } from '@/utils';
|
import { calculateAnimationDelay, isElectron, isMobile } from '@/utils';
|
||||||
|
|
||||||
import HomeListItem from './HomeListItem.vue';
|
import HomeListItem from './HomeListItem.vue';
|
||||||
|
|
||||||
@@ -88,8 +88,13 @@ const playlists = ref<any[]>([]);
|
|||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const playlistTracksMap = reactive<Record<number, any[]>>({});
|
const playlistTracksMap = reactive<Record<number, any[]>>({});
|
||||||
|
|
||||||
|
const effectiveColumns = computed(() =>
|
||||||
|
isMobile.value ? Math.min(2, props.columns) : props.columns
|
||||||
|
);
|
||||||
|
const effectiveRows = computed(() => (isMobile.value ? 2 : props.rows));
|
||||||
|
|
||||||
// Calculate display count to fill exactly N rows
|
// Calculate display count to fill exactly N rows
|
||||||
const displayCount = computed(() => props.columns * props.rows);
|
const displayCount = computed(() => effectiveColumns.value * effectiveRows.value);
|
||||||
|
|
||||||
const displayPlaylists = computed(() => {
|
const displayPlaylists = computed(() => {
|
||||||
const count = displayCount.value;
|
const count = displayCount.value;
|
||||||
@@ -97,7 +102,7 @@ const displayPlaylists = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const gridStyle = computed(() => ({
|
const gridStyle = computed(() => ({
|
||||||
gridTemplateColumns: `repeat(${props.columns}, minmax(0, 1fr))`
|
gridTemplateColumns: `repeat(${effectiveColumns.value}, minmax(0, 1fr))`
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const fetchPlaylists = async () => {
|
const fetchPlaylists = async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user