|
|
@@ -13,7 +13,7 @@ import { CommonConstants } from '../common/constants/CommonConstants';
|
|
|
import { WebDavAccount } from '../viewmodel/WebDavAccount';
|
|
|
import Logger from '../common/util/Logger';
|
|
|
import { RemoteDriveType } from '../common/enums/RemoteDriveType';
|
|
|
-import { navidromeRestApi, NavidromeRestAlbum, NavidromeRestArtist, NavidromeRestSong } from '../common/network/NavidromeRestApi';
|
|
|
+import { navidromeRestApi, NavidromeRestAlbum, NavidromeRestArtist, NavidromeRestSong, NavidromeRestPlaylist } from '../common/network/NavidromeRestApi';
|
|
|
import { Utility } from '../common/util/Utility';
|
|
|
import { Constants } from '../Constants';
|
|
|
import { EventConstants } from '../common/constants/EventConstants';
|
|
|
@@ -42,7 +42,8 @@ interface PlaylistEventData {
|
|
|
enum NavFilterType {
|
|
|
None = 0,
|
|
|
Artist = 1,
|
|
|
- Album = 2
|
|
|
+ Album = 2,
|
|
|
+ Playlist = 3
|
|
|
}
|
|
|
|
|
|
interface CachePreviewInfo {
|
|
|
@@ -77,20 +78,27 @@ export struct NavidromePage {
|
|
|
@State allVideos: VideoItem[] = [];
|
|
|
@State artists: NavidromeRestArtist[] = [];
|
|
|
@State albums: NavidromeRestAlbum[] = [];
|
|
|
+ @State playlists: NavidromeRestPlaylist[] = [];
|
|
|
@State loading: boolean = false;
|
|
|
@State songNextStart: number | null = 0;
|
|
|
@State artistNextStart: number | null = 0;
|
|
|
@State albumNextStart: number | null = 0;
|
|
|
+ @State playlistNextStart: number | null = 0;
|
|
|
@State isSongPageLoading: boolean = false;
|
|
|
@State isArtistPageLoading: boolean = false;
|
|
|
@State isAlbumPageLoading: boolean = false;
|
|
|
+ @State isPlaylistPageLoading: boolean = false;
|
|
|
+
|
|
|
+ // 新增:详情视图状态
|
|
|
+ @State isDetailView: boolean = false; // 是否在艺术家/专辑详情视图
|
|
|
+ @State previousTab: number = 0; // 进入详情视图前的标签页索引
|
|
|
@StorageProp('isDarkMode') isDarkMode: boolean = false;
|
|
|
@StorageLink('currentSong') currentSong: VideoItem | undefined = undefined;
|
|
|
@StorageProp('topSafeHeight') topSafeHeight: number = 0;
|
|
|
@State @Watch('onTabSelectedIndexesChanged') tabSelectedIndexes: number[] = [0];
|
|
|
@StorageProp('themeColor') themeColor: string =
|
|
|
PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR);
|
|
|
- private tabs: string[] = ['全部', '艺术家', '专辑'];
|
|
|
+ private tabs: string[] = ['全部', '艺术家', '专辑', '歌单'];
|
|
|
private loadTicket: number = 0;
|
|
|
@State filterType: NavFilterType = NavFilterType.None;
|
|
|
@State filterLabel: string = '';
|
|
|
@@ -118,7 +126,8 @@ export struct NavidromePage {
|
|
|
const buttons = [
|
|
|
{ text: '全部' },
|
|
|
{ text: '艺术家' },
|
|
|
- { text: '专辑' }
|
|
|
+ { text: '专辑' },
|
|
|
+ { text: '歌单' }
|
|
|
] as SegmentButtonItemTuple;
|
|
|
return SegmentButtonOptions.capsule({
|
|
|
buttons,
|
|
|
@@ -171,7 +180,13 @@ export struct NavidromePage {
|
|
|
onTabSelectedIndexesChanged() {
|
|
|
this.selectedTab = this.tabSelectedIndexes[0];
|
|
|
console.info('heanup', `Selected tab: ${this.tabs[this.selectedTab]}`);
|
|
|
-
|
|
|
+
|
|
|
+ // 如果在详情视图模式下切换标签页,退出详情视图并清除筛选
|
|
|
+ if (this.isDetailView) {
|
|
|
+ this.isDetailView = false;
|
|
|
+ this.clearFilter();
|
|
|
+ }
|
|
|
+
|
|
|
// 从艺术家或专辑切换回全部时,清除筛选状态
|
|
|
if (this.selectedTab === 0 && this.filterType !== NavFilterType.None) {
|
|
|
// 不清除筛选,保持筛选状态
|
|
|
@@ -252,15 +267,18 @@ export struct NavidromePage {
|
|
|
this.allVideos = [];
|
|
|
this.artists = [];
|
|
|
this.albums = [];
|
|
|
+ this.playlists = [];
|
|
|
this.clearFilter();
|
|
|
this.coverUrlCache.clear();
|
|
|
this.albumCoverLookup.clear();
|
|
|
this.songNextStart = 0;
|
|
|
this.artistNextStart = 0;
|
|
|
this.albumNextStart = 0;
|
|
|
+ this.playlistNextStart = 0;
|
|
|
this.isSongPageLoading = false;
|
|
|
this.isArtistPageLoading = false;
|
|
|
this.isAlbumPageLoading = false;
|
|
|
+ this.isPlaylistPageLoading = false;
|
|
|
}
|
|
|
|
|
|
private async loadMediaLibrary(account: WebDavAccount): Promise<void> {
|
|
|
@@ -290,12 +308,13 @@ export struct NavidromePage {
|
|
|
await Promise.all([
|
|
|
this.loadNextSongPage(account, ticket),
|
|
|
this.loadNextArtistPage(account, ticket),
|
|
|
- this.loadNextAlbumPage(account, ticket)
|
|
|
+ this.loadNextAlbumPage(account, ticket),
|
|
|
+ this.loadNextPlaylistPage(account, ticket)
|
|
|
]);
|
|
|
if (ticket !== this.loadTicket) {
|
|
|
return;
|
|
|
}
|
|
|
- void ServerLogUtil.info('NavidromeLoad', `首次加载完成: 歌曲 ${this.allVideos.length} / 艺术家 ${this.artists.length} / 专辑 ${this.albums.length}`);
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `首次加载完成: 歌曲 ${this.allVideos.length} / 艺术家 ${this.artists.length} / 专辑 ${this.albums.length} / 歌单 ${this.playlists.length}`);
|
|
|
await this.logCacheStatistics(account);
|
|
|
} catch (error) {
|
|
|
if (ticket === this.loadTicket) {
|
|
|
@@ -424,6 +443,20 @@ export struct NavidromePage {
|
|
|
return artist;
|
|
|
});
|
|
|
this.artists = [...this.artists, ...processed];
|
|
|
+ console.info('onecold 帮我打印这个artists的前20条数据');
|
|
|
+ // 打印前20条艺术家数据
|
|
|
+ const previewCount = Math.min(20, this.artists.length);
|
|
|
+ console.info(`onecold artists总数: ${this.artists.length}, 打印前${previewCount}条:`);
|
|
|
+ for (let i = 0; i < previewCount; i++) {
|
|
|
+ const artist = this.artists[i];
|
|
|
+ console.info(`onecold artist[${i}]:`, JSON.stringify({
|
|
|
+ id: artist.id,
|
|
|
+ name: artist.name,
|
|
|
+ albumCount: artist.albumCount,
|
|
|
+ songCount: artist.songCount,
|
|
|
+ coverUrl: artist.coverUrl
|
|
|
+ }));
|
|
|
+ }
|
|
|
this.artistNextStart = response.nextStart;
|
|
|
void ServerLogUtil.info('ArtistCover', `艺术家列表追加: 本次 ${processed.length} 位, 总数 ${this.artists.length}`);
|
|
|
} finally {
|
|
|
@@ -608,6 +641,30 @@ export struct NavidromePage {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private async loadNextPlaylistPage(account: WebDavAccount, ticket?: number): Promise<void> {
|
|
|
+ if (this.playlistNextStart === null || this.isPlaylistPageLoading) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const currentTicket = ticket ?? this.loadTicket;
|
|
|
+ this.isPlaylistPageLoading = true;
|
|
|
+ try {
|
|
|
+ const response = await navidromeRestApi.fetchPlaylistPage(account, this.playlistNextStart);
|
|
|
+ if (currentTicket !== this.loadTicket) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const chunk = response.data ?? [];
|
|
|
+ if (chunk.length === 0) {
|
|
|
+ this.playlistNextStart = null;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.playlists = [...this.playlists, ...chunk];
|
|
|
+ this.playlistNextStart = response.nextStart;
|
|
|
+ void ServerLogUtil.info('NavidromeLoad', `歌单列表追加: 本次 ${chunk.length} 个, 总数 ${this.playlists.length}`);
|
|
|
+ } finally {
|
|
|
+ this.isPlaylistPageLoading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private async handleReachEnd(): Promise<void> {
|
|
|
if (this.loading) {
|
|
|
return;
|
|
|
@@ -630,6 +687,9 @@ export struct NavidromePage {
|
|
|
case 2:
|
|
|
await this.loadNextAlbumPage(account);
|
|
|
break;
|
|
|
+ case 3:
|
|
|
+ await this.loadNextPlaylistPage(account);
|
|
|
+ break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
@@ -1160,35 +1220,63 @@ export struct NavidromePage {
|
|
|
Row({ space: 6 }) {
|
|
|
// 标题或搜索框
|
|
|
if (!this.isSearchMode) {
|
|
|
- // 左侧返回按钮
|
|
|
- Button({ type: ButtonType.Circle, stateEffect: true }) {
|
|
|
- SymbolGlyph($r('sys.symbol.sort'))
|
|
|
- .attributeModifier(new SymbolGlyphFancyModifier(25, '', ''))
|
|
|
- }
|
|
|
- .attributeModifier(new ButtonFancyModifier(40, 40))
|
|
|
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
|
|
|
- .animation({ duration: 300, curve: Curve.Ease })
|
|
|
- .onClick(() => {
|
|
|
- this.getUIContext().animateTo({ duration: 555 }, () => {
|
|
|
- // 动画闭包内控制Image组件的出现和消失
|
|
|
- this.isShowDrawer = !this.isShowDrawer
|
|
|
- this.offsetX = 0
|
|
|
- })
|
|
|
- })
|
|
|
- .attributeModifier(new ShadowModifier())
|
|
|
- .zIndex(0)
|
|
|
- Text(this.selectedAccount.name)
|
|
|
- .margin({ left: 3, right: 10 })
|
|
|
- .fontColor($r('app.color.text_color'))
|
|
|
- .fontSize(18)
|
|
|
- .maxLines(1)
|
|
|
- .textOverflow({ overflow: TextOverflow.MARQUEE })
|
|
|
- .layoutWeight(1)
|
|
|
+ // 详情视图模式下显示返回按钮
|
|
|
+ if (this.isDetailView) {
|
|
|
+ Button({ type: ButtonType.Circle, stateEffect: true }) {
|
|
|
+ SymbolGlyph($r('sys.symbol.chevron_left'))
|
|
|
+ .attributeModifier(new SymbolGlyphFancyModifier(24, '', ''))
|
|
|
+ }
|
|
|
+ .attributeModifier(new ButtonFancyModifier(40, 40))
|
|
|
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
|
|
|
+ .animation({ duration: 300, curve: Curve.Ease })
|
|
|
.onClick(() => {
|
|
|
- // 可以添加标题点击事件
|
|
|
+ // 返回到之前的标签页
|
|
|
+ this.isDetailView = false;
|
|
|
+ this.clearFilter();
|
|
|
})
|
|
|
+ .attributeModifier(new ShadowModifier())
|
|
|
+ .zIndex(0)
|
|
|
+
|
|
|
+ Text(this.filterLabel)
|
|
|
+ .margin({ left: 3, right: 10 })
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
+ .fontSize(18)
|
|
|
+ .maxLines(1)
|
|
|
+ .textOverflow({ overflow: TextOverflow.MARQUEE })
|
|
|
+ .layoutWeight(1)
|
|
|
+ .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
|
|
|
+ .animation({ duration: 300, curve: Curve.Ease })
|
|
|
+ } else {
|
|
|
+ // 正常模式:侧边栏按钮和账号名
|
|
|
+ Button({ type: ButtonType.Circle, stateEffect: true }) {
|
|
|
+ SymbolGlyph($r('sys.symbol.sort'))
|
|
|
+ .attributeModifier(new SymbolGlyphFancyModifier(25, '', ''))
|
|
|
+ }
|
|
|
+ .attributeModifier(new ButtonFancyModifier(40, 40))
|
|
|
+ .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
|
|
|
.animation({ duration: 300, curve: Curve.Ease })
|
|
|
+ .onClick(() => {
|
|
|
+ this.getUIContext().animateTo({ duration: 555 }, () => {
|
|
|
+ // 动画闭包内控制Image组件的出现和消失
|
|
|
+ this.isShowDrawer = !this.isShowDrawer
|
|
|
+ this.offsetX = 0
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .attributeModifier(new ShadowModifier())
|
|
|
+ .zIndex(0)
|
|
|
+ Text(this.selectedAccount.name)
|
|
|
+ .margin({ left: 3, right: 10 })
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
+ .fontSize(18)
|
|
|
+ .maxLines(1)
|
|
|
+ .textOverflow({ overflow: TextOverflow.MARQUEE })
|
|
|
+ .layoutWeight(1)
|
|
|
+ .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
|
|
|
+ .onClick(() => {
|
|
|
+ // 可以添加标题点击事件
|
|
|
+ })
|
|
|
+ .animation({ duration: 300, curve: Curve.Ease })
|
|
|
+ }
|
|
|
} else {
|
|
|
Button({ type: ButtonType.Circle, stateEffect: true }) {
|
|
|
SymbolGlyph($r('sys.symbol.chevron_left'))
|
|
|
@@ -1493,6 +1581,8 @@ export struct NavidromePage {
|
|
|
return this.artists.length;
|
|
|
case 2:
|
|
|
return this.albums.length;
|
|
|
+ case 3:
|
|
|
+ return this.playlists.length;
|
|
|
default:
|
|
|
return this.getVisibleSongs().length;
|
|
|
}
|
|
|
@@ -1504,6 +1594,8 @@ export struct NavidromePage {
|
|
|
return '暂无艺术家';
|
|
|
case 2:
|
|
|
return '暂无专辑';
|
|
|
+ case 3:
|
|
|
+ return '暂无歌单';
|
|
|
default:
|
|
|
if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
return this.isSearchLoading ? '正在搜索远程歌曲' : '没有匹配的远程歌曲';
|
|
|
@@ -1521,6 +1613,8 @@ export struct NavidromePage {
|
|
|
return '当前筛选没有找到艺术家';
|
|
|
case 2:
|
|
|
return '当前筛选没有找到专辑';
|
|
|
+ case 3:
|
|
|
+ return '当前筛选没有找到歌单';
|
|
|
default:
|
|
|
if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
return this.isSearchLoading ? '请稍候,正在通过 API 搜索' : '换个关键词再试试吧';
|
|
|
@@ -1549,6 +1643,10 @@ export struct NavidromePage {
|
|
|
.fillColor(song.pixelMapPath ? undefined : this.themeColor)
|
|
|
.objectFit(ImageFit.Cover)
|
|
|
.margin({ left: 20 })
|
|
|
+ .animation({
|
|
|
+ duration: 500,
|
|
|
+ curve: Curve.Friction // 可选动画曲线
|
|
|
+ })
|
|
|
.shadow({
|
|
|
radius: StrUtil.isEmpty(song.pixelMapPath) ?6:14,
|
|
|
type: ShadowType.BLUR,
|
|
|
@@ -1620,9 +1718,10 @@ export struct NavidromePage {
|
|
|
.height(48)
|
|
|
.borderRadius(10)
|
|
|
.draggable(false)
|
|
|
- .interpolation(ImageInterpolation.High)// 用于重采样后的抗锯齿
|
|
|
- .autoResize(true) // 重采样,可减少内存占用
|
|
|
- // .sourceSize({ width: 38, height: 38 })
|
|
|
+ .alt($r('app.media.alt'))
|
|
|
+ // .interpolation(ImageInterpolation.High)// 用于重采样后的抗锯齿
|
|
|
+ // .autoResize(true) // 重采样,可减少内存占用
|
|
|
+ .sourceSize({ width: 38, height: 38 })
|
|
|
.fillColor(artist.coverUrl ? undefined : this.themeColor)
|
|
|
.objectFit(ImageFit.Cover)
|
|
|
.margin({ left: 20 })
|
|
|
@@ -1666,6 +1765,7 @@ export struct NavidromePage {
|
|
|
.height(48)
|
|
|
.borderRadius(10)
|
|
|
.clip(true)
|
|
|
+ .alt($r('app.media.alt'))
|
|
|
.draggable(false)
|
|
|
// .interpolation(ImageInterpolation.High)// 用于重采样后的抗锯齿
|
|
|
// .autoResize(true) // 重采样,可减少内存占用
|
|
|
@@ -1712,6 +1812,59 @@ export struct NavidromePage {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+ @Builder
|
|
|
+ buildPlaylistItem(playlist: NavidromeRestPlaylist) {
|
|
|
+ Button({ type: ButtonType.Capsule, stateEffect: true }) {
|
|
|
+ Row({ space: 12 }) {
|
|
|
+ Image($r('app.media.alt'))
|
|
|
+ .width(48)
|
|
|
+ .height(48)
|
|
|
+ .borderRadius(10)
|
|
|
+ .alt($r('app.media.alt'))
|
|
|
+ .draggable(false)
|
|
|
+ .sourceSize({ width: 38, height: 38 })
|
|
|
+ .fillColor(this.themeColor)
|
|
|
+ .objectFit(ImageFit.Cover)
|
|
|
+ .margin({ left: 20 })
|
|
|
+
|
|
|
+ Column({ space: 4 }) {
|
|
|
+ Text(playlist.name ?? '未知歌单')
|
|
|
+ .fontSize(15)
|
|
|
+ .fontColor($r('app.color.index_tab_font_color'))
|
|
|
+ .maxLines(1)
|
|
|
+ .textAlign(TextAlign.Start)
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
+
|
|
|
+ Text(this.buildPlaylistMetaLine(playlist))
|
|
|
+ .fontSize(13)
|
|
|
+ .fontColor($r('app.color.index_tab_font_color'))
|
|
|
+ .opacity(0.6)
|
|
|
+ .maxLines(1)
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
+
|
|
|
+ if (playlist.ownerName) {
|
|
|
+ Text(playlist.ownerName)
|
|
|
+ .fontSize(12)
|
|
|
+ .fontColor($r('app.color.index_tab_font_color'))
|
|
|
+ .opacity(0.5)
|
|
|
+ .maxLines(1)
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .alignItems(HorizontalAlign.Start)
|
|
|
+ .padding({ right: 20 })
|
|
|
+ }
|
|
|
+ .width('100%')
|
|
|
+ }
|
|
|
+ .width('100%')
|
|
|
+ .padding(12)
|
|
|
+ .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.85 })
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
+ .onClick(() => {
|
|
|
+ this.onPlaylistSelected(playlist);
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
private buildSongMetaLine(song: VideoItem): string {
|
|
|
const parts: string[] = [];
|
|
|
if (song.duration) {
|
|
|
@@ -1747,6 +1900,20 @@ export struct NavidromePage {
|
|
|
return parts.join(' · ');
|
|
|
}
|
|
|
|
|
|
+ private buildPlaylistMetaLine(playlist: NavidromeRestPlaylist): string {
|
|
|
+ const parts: string[] = [];
|
|
|
+ if (playlist.songCount !== undefined) {
|
|
|
+ parts.push(`${playlist.songCount} 首歌`);
|
|
|
+ }
|
|
|
+ if (playlist.duration !== undefined) {
|
|
|
+ const duration = this.formatSongDuration(playlist.duration);
|
|
|
+ if (duration) {
|
|
|
+ parts.push(duration);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return parts.join(' · ');
|
|
|
+ }
|
|
|
+
|
|
|
private getVisibleSongs(): VideoItem[] {
|
|
|
if (this.isSearchMode) {
|
|
|
return this.searchText.length > 0 ? this.filteredList : this.allVideos;
|
|
|
@@ -1761,6 +1928,11 @@ export struct NavidromePage {
|
|
|
if (!artist || !artist.id) {
|
|
|
return;
|
|
|
}
|
|
|
+ this.isShowTitleBar = true;
|
|
|
+ // 保存当前标签页,进入详情视图
|
|
|
+ this.previousTab = this.selectedTab;
|
|
|
+ this.isDetailView = true;
|
|
|
+ // 应用筛选但不切换标签页
|
|
|
void this.applyFilter(NavFilterType.Artist, artist.id, artist.name ?? Constants.UNKNOWN_ARTIST);
|
|
|
}
|
|
|
|
|
|
@@ -1768,14 +1940,32 @@ export struct NavidromePage {
|
|
|
if (!album || !album.id) {
|
|
|
return;
|
|
|
}
|
|
|
+ this.isShowTitleBar = true;
|
|
|
+ // 保存当前标签页,进入详情视图
|
|
|
+ this.previousTab = this.selectedTab;
|
|
|
+ this.isDetailView = true;
|
|
|
+ // 应用筛选但不切换标签页
|
|
|
void this.applyFilter(NavFilterType.Album, album.id, album.name ?? '未知专辑');
|
|
|
}
|
|
|
|
|
|
+ private onPlaylistSelected(playlist: NavidromeRestPlaylist): void {
|
|
|
+ if (!playlist || !playlist.id) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.isShowTitleBar = true;
|
|
|
+ // 保存当前标签页,进入详情视图
|
|
|
+ this.previousTab = this.selectedTab;
|
|
|
+ this.isDetailView = true;
|
|
|
+ // 应用筛选但不切换标签页
|
|
|
+ void this.applyFilter(NavFilterType.Playlist, playlist.id, playlist.name ?? '未知歌单');
|
|
|
+ }
|
|
|
+
|
|
|
private async applyFilter(type: NavFilterType, id: string, label: string): Promise<void> {
|
|
|
|
|
|
// 记录筛选操作
|
|
|
void ServerLogUtil.info('NavidromeFilter', `应用筛选:`);
|
|
|
- void ServerLogUtil.info('NavidromeFilter', `- 类型: ${type} (${type === NavFilterType.Artist ? '艺术家' : type === NavFilterType.Album ? '专辑' : '无'})`);
|
|
|
+ const typeText = type === NavFilterType.Artist ? '艺术家' : type === NavFilterType.Album ? '专辑' : type === NavFilterType.Playlist ? '歌单' : '无';
|
|
|
+ void ServerLogUtil.info('NavidromeFilter', `- 类型: ${type} (${typeText})`);
|
|
|
void ServerLogUtil.info('NavidromeFilter', `- ID: ${id}`);
|
|
|
void ServerLogUtil.info('NavidromeFilter', `- 标签: ${label}`);
|
|
|
void ServerLogUtil.info('NavidromeFilter', `- 筛选前总歌曲数: ${this.allVideos.length}`);
|
|
|
@@ -1794,19 +1984,15 @@ export struct NavidromePage {
|
|
|
this.isFilterLoading = true;
|
|
|
|
|
|
// 调试日志:检查筛选结果
|
|
|
- void this.loadSongsForFilter().catch((error: Error) => {
|
|
|
+ void this.loadSongsForFilter(label).catch((error: Error) => {
|
|
|
void ServerLogUtil.error('NavidromeFilter', `筛选歌曲加载失败: ${error.message}`);
|
|
|
ToastUtil.showToast(error.message || '筛选数据加载失败');
|
|
|
});
|
|
|
|
|
|
- // 然后执行动画切换标签页
|
|
|
- this.getUIContext().animateTo({ duration: 555 }, () => {
|
|
|
- this.selectedTab = 0;
|
|
|
- this.tabSelectedIndexes = [0];
|
|
|
- })
|
|
|
+ // 不再自动切换标签页,保持在详情视图模式
|
|
|
}
|
|
|
|
|
|
- private async loadSongsForFilter(): Promise<void> {
|
|
|
+ private async loadSongsForFilter(label:string): Promise<void> {
|
|
|
if (this.filterType === NavFilterType.None || !this.filterId) {
|
|
|
this.filterSongs = [];
|
|
|
this.isFilterLoading = false;
|
|
|
@@ -1847,9 +2033,11 @@ export struct NavidromePage {
|
|
|
try {
|
|
|
let songs: NavidromeRestSong[] = [];
|
|
|
if (this.filterType === NavFilterType.Artist) {
|
|
|
- songs = await navidromeRestApi.fetchSongsByArtist(account, this.filterId);
|
|
|
+ songs = await navidromeRestApi.fetchSongsByArtist(account, this.filterId,label);
|
|
|
} else if (this.filterType === NavFilterType.Album) {
|
|
|
- songs = await navidromeRestApi.fetchSongsByAlbum(account, this.filterId);
|
|
|
+ songs = await navidromeRestApi.fetchSongsByAlbum(account, this.filterId,label);
|
|
|
+ } else if (this.filterType === NavFilterType.Playlist) {
|
|
|
+ songs = await navidromeRestApi.fetchSongsByPlaylist(account, this.filterId);
|
|
|
}
|
|
|
const videoItems = await this.convertSongsToVideoItems(songs, account);
|
|
|
if (expectedFilterId !== this.filterId || expectedFilterType !== this.filterType) {
|
|
|
@@ -1872,6 +2060,8 @@ export struct NavidromePage {
|
|
|
this.filterLabel = '';
|
|
|
this.filterSongs = [];
|
|
|
this.isFilterLoading = false;
|
|
|
+ // 退出详情视图
|
|
|
+ this.isDetailView = false;
|
|
|
}
|
|
|
|
|
|
private playSong(song: VideoItem, index: number, isJump: boolean = false): void {
|
|
|
@@ -1956,21 +2146,7 @@ export struct NavidromePage {
|
|
|
.padding({top:this.topSafeHeight+98})
|
|
|
.justifyContent(FlexAlign.Center)
|
|
|
} else {
|
|
|
- if (this.selectedTab === 0 && this.filterType !== NavFilterType.None) {
|
|
|
- Row({ space: 8 }) {
|
|
|
- Text(`筛选:${this.filterLabel}`)
|
|
|
- .fontSize(13)
|
|
|
- .fontColor(this.themeColor)
|
|
|
- .layoutWeight(1)
|
|
|
- Button('返回')
|
|
|
- .type(ButtonType.Capsule)
|
|
|
- .backgroundColor(this.themeColor)
|
|
|
- .fontSize(11)
|
|
|
- .onClick(() => this.clearFilter())
|
|
|
- }
|
|
|
- .width('90%')
|
|
|
- .padding({ left: 16, right: 16, top: this.topSafeHeight+118, bottom: 2 })
|
|
|
- }
|
|
|
+
|
|
|
|
|
|
if (this.isSearchMode && this.selectedTab === 0 && this.searchText.length > 0 && this.isSearchLoading) {
|
|
|
Column() {
|
|
|
@@ -2000,7 +2176,14 @@ export struct NavidromePage {
|
|
|
.layoutWeight(1)
|
|
|
} else {
|
|
|
List({scroller:this.scroller, space: 8 }) {
|
|
|
- if (this.selectedTab === 0) {
|
|
|
+ // 详情视图模式下显示筛选后的歌曲,否则根据标签页显示对应内容
|
|
|
+ if (this.isDetailView) {
|
|
|
+ ForEach(this.getVisibleSongs(), (item: VideoItem, index: number) => {
|
|
|
+ ListItem() {
|
|
|
+ this.buildSongItem(item, index)
|
|
|
+ }
|
|
|
+ }, (item: VideoItem) => item.id)
|
|
|
+ } else if (this.selectedTab === 0) {
|
|
|
ForEach(this.getVisibleSongs(), (item: VideoItem, index: number) => {
|
|
|
ListItem() {
|
|
|
this.buildSongItem(item, index)
|
|
|
@@ -2012,12 +2195,18 @@ export struct NavidromePage {
|
|
|
this.buildArtistItem(artist)
|
|
|
}
|
|
|
}, (artist: NavidromeRestArtist) => artist.id)
|
|
|
- } else {
|
|
|
+ } else if (this.selectedTab === 2) {
|
|
|
ForEach(this.albums, (album: NavidromeRestAlbum) => {
|
|
|
ListItem() {
|
|
|
this.buildAlbumItem(album)
|
|
|
}
|
|
|
}, (album: NavidromeRestAlbum) => album.id)
|
|
|
+ } else if (this.selectedTab === 3) {
|
|
|
+ ForEach(this.playlists, (playlist: NavidromeRestPlaylist) => {
|
|
|
+ ListItem() {
|
|
|
+ this.buildPlaylistItem(playlist)
|
|
|
+ }
|
|
|
+ }, (playlist: NavidromeRestPlaylist) => playlist.id)
|
|
|
}
|
|
|
}
|
|
|
.onScrollFrameBegin((offset: number) => {
|
|
|
@@ -2026,10 +2215,8 @@ export struct NavidromePage {
|
|
|
const currentOffsetY = this.scroller.currentOffset().yOffset;
|
|
|
// 判断滚动方向
|
|
|
if (currentOffsetY > this.prevOffsetY) {
|
|
|
- console.log("onecold 向上滚动");
|
|
|
this.isShowTitleBar = false
|
|
|
} else if (currentOffsetY < this.prevOffsetY) {
|
|
|
- console.log("onecold 向下滚动");
|
|
|
this.isShowTitleBar = true
|
|
|
}
|
|
|
// 更新前一次偏移量
|