import { VideoItem } from '../viewmodel/VideoItem'; import { LengthMetrics, SegmentButton, SegmentButtonItemTuple, SegmentButtonOptions, SymbolGlyphModifier } from '@kit.ArkUI'; import { PreferencesUtil, ToastUtil, StrUtil, ArrayUtil } from '@pura/harmony-utils'; import { ButtonFancyModifier, SymbolGlyphFancyModifier, ShadowModifier } from '../common/util/AttributeModifierUtil'; 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 { Utility } from '../common/util/Utility'; import { Constants } from '../Constants'; import { EventConstants } from '../common/constants/EventConstants'; import { emitter } from '@kit.BasicServicesKit'; import { setNavidromePlaylist } from '../common/util/NavidromePlaylistStore'; const NAVIDROME_PLAYLIST_ID = 'navidrome-playlist'; interface PlaylistEventData { playlistId: string; playlistName: string; songCount: number; startIndex: number; isJump: boolean; songFilePaths: string[]; } enum NavFilterType { None = 0, Artist = 1, Album = 2 } @Component export struct NavidromePage { @Link mType: number; @Link offsetX: number; @Link isShowDrawer: boolean; @State isNoJumpToHome: boolean = false //网盘播放不跳转首页 @Prop @Watch('onSwitchAccount') selectedAccount: WebDavAccount; @StorageProp('currentTheme') currentTheme: number = 0; @State selectedTab: number = 0; // 0: 全部, 1: 艺术家, 2: 专辑 @State allVideos: VideoItem[] = []; @State artists: NavidromeRestArtist[] = []; @State albums: NavidromeRestAlbum[] = []; @State loading: boolean = false; @StorageLink('currentSong') currentSong: VideoItem | undefined = undefined; @StorageProp('topRectHeight') topRectHeight: number = 0; @State @Watch('onTabSelectedIndexesChanged') tabSelectedIndexes: number[] = [0]; @StorageProp('themeColor') themeColor: string = PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR); private tabs: string[] = ['全部', '艺术家', '专辑']; private loadTicket: number = 0; @State filterType: NavFilterType = NavFilterType.None; @State filterLabel: string = ''; @State filterId: string = ''; // 搜索和排序相关状态 @State isSearchMode: boolean = false; @State searchText: string = ''; // 用户输入内容 @State filteredList: Array = []; // 过滤后的歌曲结果 @State sortType: number = 0; // 排序类型 0:名称升序 1:名称降序 2:时间升序 3:时间降序 4:大小升序 5:大小降序 // SegmentButton选项 @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.capsule({ buttons: [{ text: '全部' }, { text: '艺术家' }, { text: '专辑' }] as SegmentButtonItemTuple, direction: Direction.Ltr, buttonPadding: { top: 12, bottom: 12 }, backgroundColor: $r('app.color.index_background'), selectedBackgroundColor: $r('app.color.start_window_background'), selectedFontColor: $r('app.color.text_color'), fontSize: 14, selectedFontSize: 15, localizedTextPadding: { end: LengthMetrics.vp(20), start: LengthMetrics.vp(20) } }); //当胶囊按钮的选择发生变化时调用此函数 onTabSelectedIndexesChanged() { this.selectedTab = this.tabSelectedIndexes[0]; console.info('heanup', `Selected tab: ${this.tabs[this.selectedTab]}`); } //切换不同的NavidromePage async onSwitchAccount() { await this.refreshNavidromeData(true); } aboutToAppear() { this.isNoJumpToHome = PreferencesUtil.getBooleanSync('isNoJumpToHome', true) this.sortType = PreferencesUtil.getNumberSync('navidromeSortType', 0); this.refreshNavidromeData(); } private async refreshNavidromeData(showToastWhenMissing: boolean = false): Promise { const account = this.resolveActiveAccount(); if (!account) { this.resetData(); if (showToastWhenMissing) { ToastUtil.showToast('请先选择 Navidrome 账号'); } return; } await this.loadNavidromeLibrary(account); this.doSortType(this.sortType) } private resolveActiveAccount(): WebDavAccount | undefined { if (!this.selectedAccount) { return undefined; } if (this.selectedAccount.webType !== RemoteDriveType.Navidrome) { return undefined; } if (!this.selectedAccount.host || this.selectedAccount.host.length === 0) { return undefined; } return this.selectedAccount; } private resetData(): void { this.allVideos = []; this.artists = []; this.albums = []; this.clearFilter(); } private async loadNavidromeLibrary(account: WebDavAccount): Promise { const ticket = ++this.loadTicket; this.loading = true; try { const requestTasks: Promise[] = [ navidromeRestApi.fetchAllSongs(account), navidromeRestApi.fetchArtists(account), navidromeRestApi.fetchAlbums(account) ]; const responses = await Promise.all(requestTasks); const songs = responses[0] as NavidromeRestSong[]; const artistList = responses[1] as NavidromeRestArtist[]; const albumList = responses[2] as NavidromeRestAlbum[]; if (ticket !== this.loadTicket) { return; } this.allVideos = songs.map(song => this.convertSongToVideoItem(song, account)); this.artists = artistList; this.albums = albumList; Logger.info('heanup', `Navidrome 已加载: 歌曲 ${songs.length} 首, 艺术家 ${artistList.length} 位, 专辑 ${albumList.length} 张`); } catch (error) { if (ticket === this.loadTicket) { Logger.error('heanup', `Navidrome 数据加载失败: ${(error as Error).message}`); ToastUtil.showToast((error as Error).message ?? 'Navidrome 数据加载失败'); } } finally { if (ticket === this.loadTicket) { this.loading = false; } } } private convertSongToVideoItem(song: NavidromeRestSong, account: WebDavAccount): VideoItem { const title = song.title ?? Constants.UNKNOWN_TITLE; const videoItem = new VideoItem( title, song.id, `navidrome://${account.id ?? 0}/${song.id}`, CommonConstants.TYPE_NAVIDROME, song.size ?? 0, song.createdAt ?? '', Utility.formatFSize(song.size ?? 0), undefined, song.artist ?? Constants.UNKNOWN_ARTIST, song.album ?? '', `${title}${song.suffix ? '.' + song.suffix : ''}` ); const durationStr = this.formatSongDuration(song.duration); if (durationStr) { videoItem.duration = durationStr; } videoItem.size = Utility.formatFSize(song.size ?? 0); videoItem.bit_rate = song.bitRate ? song.bitRate.toString() : undefined; videoItem.genre = song.genre; videoItem.webdav_account_id = account.id?.toString(); videoItem.remote_rel_path = song.id; videoItem.navArtistId = song.artistId; videoItem.navAlbumId = song.albumId; return videoItem; } private formatSongDuration(durationSeconds?: number): string | undefined { if (durationSeconds === undefined || durationSeconds === null || durationSeconds < 0) { return undefined; } const totalSeconds = Math.floor(durationSeconds); const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; const pad = (value: number) => value.toString().padStart(2, '0'); return `${pad(minutes)}:${pad(seconds)}`; } @Builder topTitleBar() { Column() { 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('Navidrome音乐') .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')) .attributeModifier(new SymbolGlyphFancyModifier(24, '', '')) } .attributeModifier(new ButtonFancyModifier(40, 40)) .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 }) .animation({ duration: 300, curve: Curve.Ease }) .onClick(() => { this.isSearchMode = false; this.onSearchInput(''); }) .attributeModifier(new ShadowModifier()) .zIndex(0) } // 搜索框 Search({ value: this.searchText, placeholder: '搜索标题、艺术家...' }) .searchButton('搜索', { fontColor: this.themeColor }) .searchIcon({ src: $r('sys.media.ohos_ic_public_search_filled') }) .cancelButton({ style: CancelButtonStyle.CONSTANT, icon: { src: $r('sys.media.ohos_ic_public_cancel_filled') } }) .layoutWeight(1) .height(35) .maxLength(20) .backgroundColor(Color.White) .placeholderColor(Color.Grey) .placeholderFont({ size: 14, weight: 400 }) .textFont({ size: 14, weight: 400 }) .onSubmit((value: string) => { this.onSearchInput(value); }) .onChange((value: string) => { this.onSearchInput(value); }) .visibility(this.isSearchMode?Visibility.Visible:Visibility.None) .animation({ duration: 300, curve: Curve.Ease }) // 搜索/排序按钮 if (!this.isSearchMode) { // 搜索按钮 Button({ type: ButtonType.Circle, stateEffect: true }) { SymbolGlyph($r('sys.symbol.magnifyingglass')) .attributeModifier(new SymbolGlyphFancyModifier(25, '', '')) } .attributeModifier(new ButtonFancyModifier(40, 40)) .animation({ duration: 300, curve: Curve.Ease }) .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 }) .attributeModifier(new ShadowModifier()) .zIndex(0) .visibility(this.selectedTab==0?Visibility.Visible:Visibility.None) .onClick(() => { this.isSearchMode = true; if(ArrayUtil.isEmpty(this.filteredList)){ this.filteredList = [...this.allVideos]; } }) // 排序按钮 Button({ type: ButtonType.Circle, stateEffect: true }) { SymbolGlyph($r('sys.symbol.list_number')) .attributeModifier(new SymbolGlyphFancyModifier(25, '', '')) } .attributeModifier(new ButtonFancyModifier(40, 40)) .animation({ duration: 300, curve: Curve.Ease }) .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 }) .bindMenu(this.SortMenuBuilder) .attributeModifier(new ShadowModifier()) .zIndex(0) } } .width('100%') .height(55) .padding({ left: 15, right: 15 }) .justifyContent(FlexAlign.SpaceBetween) .alignItems(VerticalAlign.Center) // 分段按钮 SegmentButton({ options: this.tabOptions, selectedIndexes: $tabSelectedIndexes }) .width('100%') .padding({ left: 25, right: 25, top: 5, bottom: 5 }) } .width('100%') .padding({ top: this.topRectHeight + 5 }) .backgroundColor($r('app.color.start_window_background')) } @Builder SortMenuBuilder() { Menu() { MenuItem({ symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.text_and_arrow_up')), content: $r('app.string.sort_by_name') }) .onClick(async () => { this.doSortType(0); PreferencesUtil.put("navidromeSortType", 0); }) MenuItem({ symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.text_and_arrow_down')), content: '按名称降序' }) .onClick(async () => { this.doSortType(1); PreferencesUtil.put("navidromeSortType", 1); }) MenuItem({ symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.clock')), content: '按艺术家升序' }) .onClick(async () => { this.doSortType(2); PreferencesUtil.put("navidromeSortType", 2); }) MenuItem({ symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.timer')), content: '按艺术家降序' }) .onClick(async () => { this.doSortType(3); PreferencesUtil.put("navidromeSortType", 3); }) MenuItem({ symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.text_and_arrow_up')), content: '按专辑升序' }) .onClick(async () => { this.doSortType(4); PreferencesUtil.put("navidromeSortType", 4); }) MenuItem({ symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.text_and_arrow_down')), content: '按专辑降序' }) .onClick(async () => { this.doSortType(5); PreferencesUtil.put("navidromeSortType", 5); }) } } doSortType(index: number) { this.sortType = index; const songs = this.isSearchMode ? this.filteredList :this.allVideos; // 对歌曲列表进行排序 switch (index) { case 0: // 名称升序 Utility.doSortListAscending(songs,false) break; case 1: // 名称降序 Utility.doSortListDescending(songs,true) break; case 2: // 艺术家升序 songs.sort((a, b) => { // 处理艺术家可能为undefined的字符串比较 const artistA = a.artist?.trim() || ''; // 可选添加 trim() 处理空格 const artistB = b.artist?.trim() || ''; return artistA.localeCompare(artistB); }); break; case 3: // 艺术家降序 songs.sort((a, b) => { // 处理艺术家可能为undefined的字符串比较 const artistA = a.artist?.trim() || ''; // 可选添加 trim() 处理空格 const artistB = b.artist?.trim() || ''; return artistB.localeCompare(artistA); }); break; case 4: // 专辑升序 songs.sort((a, b) => { const albumA = a.album?.trim() || ''; // 可选添加 trim() 处理空格 const albumB = b.album?.trim() || ''; return albumA.localeCompare(albumB); }); break; case 5: // 专辑降序 songs.sort((a, b) => { const albumA = a.album?.trim() || ''; // 可选添加 trim() 处理空格 const albumB = b.album?.trim() || ''; return albumB.localeCompare(albumA); }); break; } // 更新显示列表 if (this.isSearchMode) { this.filteredList = [...songs]; } } // 实时搜索逻辑 private onSearchInput(value: string) { this.searchText = value.trim(); let mSearchList: Array = [...this.allVideos]; // 新增条件判断:空输入时显示所有数据 if (this.searchText === '') { this.filteredList = [...mSearchList]; // 创建新数组以触发状态更新 } else { this.filteredList = mSearchList.filter((item: VideoItem) => { // 支持模糊匹配和艺术家 专辑匹配 const regex = new RegExp(this.searchText.toLowerCase().replace(/\s+/g, '.*'), 'i'); return regex.test(item.name.toLowerCase()) || regex.test(item.fileName?.toLowerCase() ?? "") || regex.test(item.artist?.toLowerCase() ?? "") || regex.test(item.album?.toLowerCase() ?? "") }); } } private getCurrentCount(): number { switch (this.selectedTab) { case 1: return this.artists.length; case 2: return this.albums.length; default: return this.getVisibleSongs().length; } } private getEmptyTitle(): string { switch (this.selectedTab) { case 1: return '暂无艺术家'; case 2: return '暂无专辑'; default: return this.filterType === NavFilterType.None ? '暂无音乐' : '该筛选下暂无歌曲'; } } private getEmptySubtitle(): string { switch (this.selectedTab) { case 1: return '当前筛选没有找到艺术家'; case 2: return '当前筛选没有找到专辑'; default: return this.filterType === NavFilterType.None ? '当前分类下没有找到音乐文件' : '请尝试调整筛选条件'; } } @Builder buildSongItem(song: VideoItem, index: number) { Button({ type: ButtonType.Normal, stateEffect: false }) { Row({ space: 12 }) { // 歌曲封面 Image(song.pixelMapPath ? song.pixelMapPath : $r('app.media.music_red')) .width(48) .height(48) .borderRadius(10) .sourceSize({ width: 38, height: 38 }) .alt($r('app.media.music_red')) .fillColor(this.themeColor) .objectFit(ImageFit.Cover) .margin({ left: 8 }) .onClick(() => { this.playSong(song, index, true); }) // 歌曲信息 Column({ space: 4 }) { Text(song.name) .fontSize(15) .fontColor(this.currentSong?.filePath == song.filePath ? this.themeColor : $r('app.color.index_tab_font_color')) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }) Row() { Text((song.artist ?? '') + " ") .fontSize(13) .fontColor(this.currentSong?.filePath == song.filePath ? this.themeColor : $r('app.color.index_tab_font_color')) .opacity(0.6) .maxLines(1) .visibility(song.artist ? Visibility.Visible : Visibility.None) .textOverflow({ overflow: TextOverflow.Ellipsis }) Text(this.buildSongMetaLine(song)) .fontSize(13) .fontColor(this.currentSong?.filePath == song.filePath ? this.themeColor : $r('app.color.index_tab_font_color')) .opacity(0.6) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }) } .width('90%') } .alignItems(HorizontalAlign.Start) .layoutWeight(1) .padding({ right: 20 }) Column() { ImageAnimator() .images(CommonConstants.IMAGE_FRAME_INFO)// 动画数组 .duration(1000)// 持续 .state(AnimationStatus.Running)// 动画状态 .fillMode(FillMode.Forwards) .visibility(this.currentSong?.filePath == song.filePath ? Visibility.Visible : Visibility.None) .width(18) .margin({ right: 12, top: 8, bottom: 8 }) .height(18) .iterations(-1) // 播放次数 } } } .width('100%') .padding(12) .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 }) .backgroundColor(Color.Transparent) .onClick(() => { this.playSong(song, index); }) } @Builder buildArtistItem(artist: NavidromeRestArtist) { Button({ type: ButtonType.Normal, stateEffect: false }) { Row({ space: 12 }) { Image($r('app.media.music_red')) .width(48) .height(48) .borderRadius(10) .margin({ left: 8 }) Column({ space: 4 }) { Text(artist.name ?? Constants.UNKNOWN_ARTIST) .fontSize(15) .fontColor($r('app.color.index_tab_font_color')) .maxLines(1) .textAlign(TextAlign.Start) .textOverflow({ overflow: TextOverflow.Ellipsis }) Text(this.buildArtistMetaLine(artist)) .fontSize(13) .fontColor($r('app.color.index_tab_font_color')) .opacity(0.6) .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.onArtistSelected(artist); }) } @Builder buildAlbumItem(album: NavidromeRestAlbum) { Button({ type: ButtonType.Normal, stateEffect: false }) { Row({ space: 12 }) { Image($r('app.media.music_red')) .width(48) .height(48) .borderRadius(10) .margin({ left: 8 }) Column({ space: 4 }) { Text(album.name ?? '未知专辑') .fontSize(15) .fontColor($r('app.color.index_tab_font_color')) .maxLines(1) .textAlign(TextAlign.Start) .textOverflow({ overflow: TextOverflow.Ellipsis }) Row() { Text(album.artist ?? Constants.UNKNOWN_ARTIST) .fontSize(13) .fontColor($r('app.color.index_tab_font_color')) .opacity(0.6) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }) Text(this.buildAlbumMetaLine(album)) .fontSize(13) .fontColor($r('app.color.index_tab_font_color')) .opacity(0.6) .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.onAlbumSelected(album); }) } private buildSongMetaLine(song: VideoItem): string { const parts: string[] = []; if (song.duration) { parts.push(song.duration as string); } if (song.size) { parts.push(song.size as string); } if (parts.length === 0 && song.cTime) { parts.push(song.cTime as string); } return parts.join(' · '); } private buildArtistMetaLine(artist: NavidromeRestArtist): string { const albumCount = artist.albumCount ?? 0; const songCount = artist.songCount ?? 0; const playCount = artist.playCount ?? 0; return `专辑 ${albumCount} · 歌曲 ${songCount} · 播放 ${playCount}`; } private buildAlbumMetaLine(album: NavidromeRestAlbum): string { const parts: string[] = []; if (album.songCount !== undefined) { parts.push(`歌曲 ${album.songCount}`); } if (album.duration !== undefined) { const duration = this.formatSongDuration(album.duration); if (duration) { parts.push(duration); } } if (album.minYear) { parts.push(`发行 ${album.minYear}`); } return parts.join(' · '); } private getVisibleSongs(): VideoItem[] { if(this.isSearchMode) return this.filteredList; if (this.filterType === NavFilterType.Artist && this.filterId.length > 0) { return this.allVideos.filter(item => item.navArtistId === this.filterId || item.artist === this.filterLabel); } if (this.filterType === NavFilterType.Album && this.filterId.length > 0) { return this.allVideos.filter(item => item.navAlbumId === this.filterId || item.album === this.filterLabel); } return this.allVideos; } private onArtistSelected(artist: NavidromeRestArtist): void { if (!artist || !artist.id) { return; } this.applyFilter(NavFilterType.Artist, artist.id, artist.name ?? Constants.UNKNOWN_ARTIST); } private onAlbumSelected(album: NavidromeRestAlbum): void { if (!album || !album.id) { return; } this.applyFilter(NavFilterType.Album, album.id, album.name ?? '未知专辑'); } private applyFilter(type: NavFilterType, id: string, label: string): void { this.getUIContext().animateTo({ duration: 555 }, () => { this.filterType = type; this.filterId = id; this.filterLabel = label; this.selectedTab = 0; this.tabSelectedIndexes = [0]; }) } private clearFilter(): void { this.filterType = NavFilterType.None; this.filterId = ''; this.filterLabel = ''; } private playSong(song: VideoItem, index: number, isJump: boolean = false): void { try { if (!this.allVideos || this.allVideos.length === 0) { ToastUtil.showToast('暂无可播放的歌曲'); return; } const account = this.resolveActiveAccount(); if (!account || !account.id) { ToastUtil.showToast('Navidrome账号信息不完整,无法播放'); return; } Logger.info('heanup', `Navidrome 播放歌曲: ${song.name}, 索引: ${index}`); const targetIndex = this.allVideos.findIndex(item => item.id === song.id); const startIndex = targetIndex >= 0 ? targetIndex : index; setNavidromePlaylist(this.allVideos, startIndex); const playlistData: PlaylistEventData = { playlistId: NAVIDROME_PLAYLIST_ID, playlistName: `Navidrome - ${account.name ?? '未知账户'}`, songCount: this.allVideos.length, startIndex, isJump: isJump,//设置true会弹出播放页 songFilePaths: this.allVideos.map(item => item.filePath) }; const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY }; emitter.emit(eventPlaylistPlay, { data: playlistData }); Logger.info('heanup', `Navidrome 发送播放事件,歌曲数: ${this.allVideos.length}, 起始: ${index}`); if(!this.isNoJumpToHome){ // 跳转到首页播放器 this.getUIContext()?.animateTo({ duration: 555 }, () => { this.mType = 0 }) } } catch (error) { const err = error as Error; Logger.error('heanup', '播放歌曲失败: ' + err.message); ToastUtil.showToast('播放失败'); } } build() { Column() { this.topTitleBar() // 主内容区域 if (this.loading) { Column() { LoadingProgress() .width(50) .height(50) .color($r('app.color.title_bar_bg')) Text('加载中...') .margin({ top: 10 }) .fontSize(14) .fontColor($r('app.color.index_tab_unselected_font_color')) } .width('100%') .layoutWeight(1) .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(12) .onClick(() => this.clearFilter()) } .width('90%') .padding({ left: 16, right: 16, top: 6, bottom: 2 }) } List({ space: 8 }) { if (this.selectedTab === 0) { ForEach(this.getVisibleSongs(), (item: VideoItem, index: number) => { ListItem() { this.buildSongItem(item, index) } }, (item: VideoItem) => item.id) } else if (this.selectedTab === 1) { ForEach(this.artists, (artist: NavidromeRestArtist) => { ListItem() { this.buildArtistItem(artist) } }, (artist: NavidromeRestArtist) => artist.id) } else { ForEach(this.albums, (album: NavidromeRestAlbum) => { ListItem() { this.buildAlbumItem(album) } }, (album: NavidromeRestAlbum) => album.id) } } .width('100%') .layoutWeight(1) .padding({ top: 10, bottom: 10 }) .listDirection(Axis.Vertical) .scrollBar(BarState.Auto) .edgeEffect(EdgeEffect.Spring) // 空状态 if (this.getCurrentCount() === 0) { Column() { Image($r('app.media.music_red')) .width(80) .height(80) .opacity(0.6) Text(this.getEmptyTitle()) .margin({ top: 16 }) .fontSize(16) .fontColor($r('app.color.index_tab_unselected_font_color')) Text(this.getEmptySubtitle()) .margin({ top: 8 }) .fontSize(14) .fontColor($r('app.color.index_tab_unselected_font_color')) } .width('100%') .layoutWeight(1) .justifyContent(FlexAlign.Center) } } } .width('100%') .height('100%') .backgroundColor($r('app.color.start_window_background')) } }