Explorar o código

上传NavidromePage页面

onecold hai 9 meses
pai
achega
2d57d7eaa7

+ 16 - 2
entry/src/main/ets/pages/NewIndex.ets

@@ -52,6 +52,7 @@ import { RemoteDriveAccountDialog } from '../dialog/RemoteDriveAccountDialog';
 import ReqPermissionUtil from '../common/util/ReqPermissionUtil';
 import { getRemoteDriveAccountLabel, getRemoteDriveDisplayLabel, getRemoteDriveProtocolLabel } from '../common/util/RemoteDriveLabel';
 import { RemoteDriveType } from '../common/enums/RemoteDriveType';
+import { NavidromePage } from '../view/NavidromePage';
 
 // import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 
@@ -406,7 +407,15 @@ struct NewIndex {
             selectedAccount:this.selectedAccount
           })
             .visibility(this.mType === 6 ? Visibility.Visible : Visibility.None)
-          
+          if(this.mType === 7){
+            NavidromePage({
+              offsetX:this.offsetX,
+              isShowDrawer:this.isShowDrawer,
+              mType:this.mType,
+              selectedAccount:this.selectedAccount
+            })
+              .visibility(this.mType === 7 ? Visibility.Visible : Visibility.None)
+          }
           // 抽屉打开时的遮罩层,用于拦截点击事件  这个只能正常尺寸的手机竖屏的才能生效
           if (this.isShowDrawer&&this.isPhonePortrait()) {
             Column()
@@ -1641,8 +1650,13 @@ struct NewIndex {
         })
       }
       this.selectedAccount = account
+      console.info('onecold selectWebDavAccount account.webType='+account.webType)
       // 切换到WebDAV页面
-      this.mType = 6
+      if(account.webType==RemoteDriveType.Navidrome){
+        this.mType = 7
+      }else{
+        this.mType = 6
+      }
       this.doShowDrawer()
     } catch (error) {
       LogUtil.error('heanup NewIndex', `选择网盘账户失败: ${(error as Error).message}`)

+ 7 - 4
entry/src/main/ets/view/LocalMusic.ets

@@ -2196,9 +2196,9 @@ export struct LocalMusic {
       animateTo({ duration: 666 }, () => {
         this.opacityItem = 1;
       });
+      this.setButtonStatus()
       //添加到字母索引表
       this.setAlphaBet()
-      this.setButtonStatus()
     }, 200);
 
   }
@@ -3523,7 +3523,8 @@ export struct LocalMusic {
     let targetIndex = -1;
     for (let i = 0; i < songs.length; i++) {
       let song = songs[i]
-      let text = this.isShowFileName?song.fileName:song.name
+      let text = this.isShowFileName?song.fileName:
+        (song.type!=CommonConstants.TYPE_IS_DIR&&song.pyStr)?song.pyStr:song.name
       if(this.sortType === 0||this.sortType==1){
         text = song.artist
       }else if(this.sortType === 2||this.sortType==3){
@@ -3544,7 +3545,8 @@ export struct LocalMusic {
   }
   // 找到对应字母表的索引
   findAlphaBetIndex(song: VideoItem):number{
-    let text = this.isShowFileName?song.fileName:song.name
+    let text = this.isShowFileName?song.fileName:
+      (song.type!=CommonConstants.TYPE_IS_DIR&&song.pyStr)?song.pyStr:song.name
     if(this.sortType === 0||this.sortType==1){
       text = song.artist
     }else if(this.sortType === 2||this.sortType==3){
@@ -3573,7 +3575,8 @@ export struct LocalMusic {
         }
         let alphabetSet = new Set<string>();
         for (let song of songs) {
-          let text = this.isShowFileName?song.fileName:song.name
+          let text = this.isShowFileName?song.fileName:
+            (song.type!=CommonConstants.TYPE_IS_DIR&&song.pyStr)?song.pyStr:song.name
           if(this.sortType === 0||this.sortType==1){
             text = song.artist
           }else if(this.sortType === 2||this.sortType==3){

+ 308 - 0
entry/src/main/ets/view/NavidromePage.ets

@@ -0,0 +1,308 @@
+import { router } from '@kit.ArkUI';
+import { VideoItem } from '../viewmodel/VideoItem';
+import { LengthMetrics, SegmentButton, SegmentButtonItemTuple, SegmentButtonOptions } from '@kit.ArkUI';
+import {
+  PreferencesUtil, ToastUtil
+} from '@pura/harmony-utils';
+import {
+  ButtonFancyModifier,
+  SymbolGlyphFancyModifier,
+  ShadowModifier
+} from '../common/util/AttributeModifierUtil';
+import { CommonConstants } from '../common/constants/CommonConstants';
+import { WebDavAccount } from '../viewmodel/WebDavAccount';
+
+@Component
+export struct NavidromePage {
+  @Link mType: number;
+  @Link offsetX: number;
+  @Link isShowDrawer: boolean;
+  @Prop @Watch('onSwitchAccount') selectedAccount: WebDavAccount;
+  @StorageProp('currentTheme') currentTheme: number = 0;
+  @State selectedTab: number = 0; // 0: 全部, 1: 艺术家, 2: 专辑
+  @State allVideos: VideoItem[] = [];
+  @State artistVideos: VideoItem[] = [];
+  @State albumVideos: VideoItem[] = [];
+  @State loading: boolean = false;
+  @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[] = ['全部', '艺术家', '专辑'];
+
+  // SegmentButton选项
+  @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.capsule({
+    buttons: [{ text: '全部' }, { text: '艺术家' }, { text: '专辑' }] as SegmentButtonItemTuple,
+    direction: Direction.Ltr,
+    buttonPadding: { top: 12, bottom: 12 },
+    backgroundColor: $r('app.color.start_window_background'),
+    fontSize: 14,
+    selectedFontSize: 15,
+    selectedFontColor: Color.White,
+    selectedBackgroundColor: $r('app.color.title_bar_bg'),
+    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(){
+
+  }
+
+  aboutToAppear() {
+    this.initMockData();
+  }
+
+  private initMockData() {
+    // 模拟全部数据
+    this.allVideos = [
+      new VideoItem('夜曲', '1', 'http://example.com/music1.mp3', CommonConstants.TYPE_NAVIDROME, 1024000, '2024-01-01', '1.0MB', '', '周杰伦', '十一月的萧邦', '夜曲.mp3', new Date('2024-01-15')),
+      new VideoItem('青花瓷', '2', 'http://example.com/music2.mp3', CommonConstants.TYPE_NAVIDROME, 2048000, '2024-01-02', '2.0MB', '', '周杰伦', '我很忙', '青花瓷.mp3', new Date('2024-01-14')),
+      new VideoItem('稻香', '3', 'http://example.com/music3.mp3', CommonConstants.TYPE_NAVIDROME, 1536000, '2024-01-03', '1.5MB', '', '周杰伦', '魔杰座', '稻香.mp3', new Date('2024-01-13')),
+      new VideoItem('告白气球', '4', 'http://example.com/music4.mp3', CommonConstants.TYPE_NAVIDROME, 2560000, '2024-01-04', '2.5MB', '', '周杰伦', '周杰伦的床边故事', '告白气球.mp3', new Date('2024-01-12')),
+      new VideoItem('演员', '5', 'http://example.com/music5.mp3', CommonConstants.TYPE_NAVIDROME, 1280000, '2024-01-05', '1.3MB', '', '薛之谦', '绅士', '演员.mp3', new Date('2024-01-11')),
+      new VideoItem('丑八怪', '6', 'http://example.com/music6.mp3', CommonConstants.TYPE_NAVIDROME, 1792000, '2024-01-06', '1.8MB', '', '薛之谦', '意外', '丑八怪.mp3', new Date('2024-01-10'))
+    ];
+
+    // 模拟艺术家数据 - 筛选周杰伦的歌曲
+    this.artistVideos = this.allVideos.filter(item => item.artist === '周杰伦');
+
+    // 模拟专辑数据 - 筛选十一月的萧邦专辑的歌曲
+    this.albumVideos = this.allVideos.filter(item => item.album === '十一月的萧邦');
+  }
+
+  @Builder
+  topTitleBar() {
+    Column() {
+      Row({ space: 15 }) {
+        // 左侧返回按钮
+        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 })
+
+
+      }
+      .width('100%')
+      .height(60)
+      .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 })
+        .margin({top:5})
+    }
+    .width('100%')
+    .padding({ top: this.topRectHeight+10})
+    .backgroundColor($r('app.color.start_window_background'))
+  }
+
+  private getCurrentVideos(): VideoItem[] {
+    switch (this.selectedTab) {
+      case 1: // 艺术家
+        return this.artistVideos;
+      case 2: // 专辑
+        return this.albumVideos;
+      default: // 全部
+        return this.allVideos;
+    }
+  }
+
+  @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 })
+
+        // 歌曲信息
+        Column({ space: 4 }) {
+          Text(song.name)
+            .fontSize(15)
+            .fontColor(this.themeColor)
+            .maxLines(1)
+            .textOverflow({ overflow: TextOverflow.Ellipsis })
+
+          Row(){
+            Text((song.artist ?? '') + "  ")
+              .fontSize(13)
+              .fontColor($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($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)
+            .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);
+    })
+  }
+
+  private buildSongMetaLine(song: VideoItem): string {
+    const parts: string[] = [];
+    if (song.duration) {
+      parts.push(song.duration as string);
+    } else 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 playSong(song: VideoItem, index: number): void {
+    try {
+      console.info('heanup', `Navidrome 播放歌曲: ${song.name}, 索引: ${index}`);
+
+      // TODO: 实现Navidrome播放功能
+      // 这里可以参考WebDavMainPage中的playSong方法
+
+
+    } catch (error) {
+      const err = error as Error;
+      console.error('heanup', '播放歌曲失败: ' + err.message);
+
+    }
+  }
+
+  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 {
+        List({ space: 8 }) {
+          ForEach(this.getCurrentVideos(), (item: VideoItem, index: number) => {
+            ListItem() {
+              this.buildSongItem(item, index)
+            }
+          })
+        }
+        .width('100%')
+        .layoutWeight(1)
+        .padding({ top: 10, bottom: 10 })
+        .listDirection(Axis.Vertical)
+        .scrollBar(BarState.Auto)
+        .edgeEffect(EdgeEffect.Spring)
+
+        // 空状态
+        if (this.getCurrentVideos().length === 0) {
+          Column() {
+            Image($r('app.media.music_red'))
+              .width(80)
+              .height(80)
+              .opacity(0.6)
+
+            Text('暂无音乐')
+              .margin({ top: 16 })
+              .fontSize(16)
+              .fontColor($r('app.color.index_tab_unselected_font_color'))
+
+            Text('当前分类下没有找到音乐文件')
+              .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'))
+  }
+}