chendeben 10 mesi fa
parent
commit
e6b3fe31b4

+ 161 - 91
entry/src/main/ets/pages/PlaylistDetailPage.ets

@@ -3,11 +3,12 @@ import { VideoItem } from '../viewmodel/VideoItem';
 import PlaylistTable from '../common/util/PlaylistTable';
 import MediaTable from '../common/util/MediaTable';
 import { emitter } from '@kit.BasicServicesKit';
-import { ToastUtil, AppUtil, LogUtil } from '@pura/harmony-utils';
+import { ToastUtil, AppUtil, LogUtil, PreferencesUtil } from '@pura/harmony-utils';
 import { showEditPlaylistDialog } from '../dialog/PlaylistDialog';
 import { showAddSongsToPlaylistDialog } from '../dialog/AddSongsToPlaylistDialog';
 import { router } from '@kit.ArkUI';
 import { GlobalContext } from '../common/util/GlobalContext';
+import { CommonConstants } from '../common/constants/CommonConstants';
 
 /**
  * 简化的歌单播放事件数据
@@ -40,8 +41,12 @@ export struct PlaylistDetailPage {
   private playlistTable: PlaylistTable = new PlaylistTable(getContext(this))
   private mediaTable: MediaTable = new MediaTable(getContext(this))
   private playlistId: string = ''
+  @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
 
   aboutToAppear() {
+    let themeColor = PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR);
+    AppStorage.setOrCreate('themeColor', themeColor);
+    this.themeColor = themeColor
     // 获取传入的歌单对象
     const params = router.getParams() as Record<string, Object>
     if (params && params['playlist']) {
@@ -155,14 +160,29 @@ export struct PlaylistDetailPage {
       // 监听播放状态变化事件
       const eventPlaybackStatus: emitter.InnerEvent = { eventId: 2003 }
       emitter.on(eventPlaybackStatus, (eventData: emitter.EventData) => {
-        LogUtil.info('heanup PlaylistDetailPage 收到播放状态变化事件')
+        LogUtil.info('heanup PlaylistDetailPage 收到播放状态变化事件'+ JSON.stringify(eventData))
         if (eventData.data) {
           const data = eventData.data as Record<string, Object>
-          this.isPlaying = data['isPlaying'] as boolean || false
-          this.curIndex = data['curIndex'] as number || -1
+          const oldIsPlaying = this.isPlaying
+          const oldCurIndex = this.curIndex
+          
+          // 添加调试日志
+
+          this.isPlaying = data['isPlaying'] as boolean
+          let newCurIndex = data['curIndex'] as number
+          
+          LogUtil.info(`heanup 原始播放状态: isPlaying=${this.isPlaying}, curIndex=${newCurIndex}`)
+
+          
+          this.curIndex = newCurIndex
 
-          // 如果当前播放的是这个歌单的歌曲,高亮显示
-          LogUtil.info(`heanup 播放状态更新: isPlaying=${this.isPlaying}, curIndex=${this.curIndex}`)
+          LogUtil.info(`heanup 播放状态更新: isPlaying=${oldIsPlaying}->${this.isPlaying}, curIndex=${oldCurIndex}->${this.curIndex}`)
+          
+          // 检查当前播放的歌曲是否在当前歌单中
+          if (this.curIndex >= 0 && this.curIndex < this.songList.length) {
+            const currentSong = this.songList[this.curIndex]
+            LogUtil.info(`heanup 当前播放歌曲: ${currentSong.name}, 文件路径: ${currentSong.filePath}`)
+          }
         }
       })
     } catch (error) {
@@ -412,15 +432,11 @@ export struct PlaylistDetailPage {
             .fontWeight(FontWeight.Medium)
             .layoutWeight(1)
             .textAlign(TextAlign.Center)
-          
-          // 更多操作按钮
-          Image($r('app.media.ic_more_vert_black_24dp'))
+
+          // 占位空间,保持标题居中
+          Blank()
             .width(24)
-            .height(24)
             .margin({ right: 12 })
-            .onClick(() => {
-              this.showMoreMenu()
-            })
         }
         .height(48)
         .width('100%')
@@ -434,7 +450,7 @@ export struct PlaylistDetailPage {
           LoadingProgress()
             .width(40)
             .height(40)
-            .color($r('app.color.theme_color'))
+            .color(this.themeColor )
           
           Text('加载中...')
             .fontSize(14)
@@ -519,7 +535,7 @@ export struct PlaylistDetailPage {
           .padding({ right: 24 })
 
           // 操作按钮区域
-          Row({ space: 16 }) {
+          Row({ space: 12 }) {
             // 播放全部按钮 - 参考LocalMusic的按钮样式
             Button() {
               Row({ space: 8 }) {
@@ -535,9 +551,9 @@ export struct PlaylistDetailPage {
               }
               .justifyContent(FlexAlign.Center)
             }
-            .width(140)
+            .layoutWeight(1)
             .height(44)
-            .backgroundColor($r('app.color.theme_color'))
+            .backgroundColor(this.themeColor )
             .borderRadius(22)
             .shadow({
               radius: 8,
@@ -550,7 +566,7 @@ export struct PlaylistDetailPage {
             })
             .stateStyles({
               normal: {
-                .backgroundColor($r('app.color.theme_color'))
+                .backgroundColor(this.themeColor )
                 .scale({ x: 1, y: 1 })
               },
               pressed: {
@@ -568,22 +584,22 @@ export struct PlaylistDetailPage {
               Row({ space: 6 }) {
                 Text('✏️')
                   .fontSize(16)
-                  .fontColor($r('app.color.theme_color'))
+                  .fontColor(this.themeColor )
 
                 Text('编辑')
                   .fontSize(14)
-                  .fontColor($r('app.color.theme_color'))
+                  .fontColor(this.themeColor )
                   .fontWeight(FontWeight.Medium)
               }
               .justifyContent(FlexAlign.Center)
             }
-            .width(100)
+            .layoutWeight(1)
             .height(44)
             .backgroundColor(Color.Transparent)
             .borderRadius(22)
             .border({
               width: 1.5,
-              color: $r('app.color.theme_color')
+              color: this.themeColor
             })
             .onClick(() => {
               this.editPlaylist()
@@ -603,19 +619,26 @@ export struct PlaylistDetailPage {
               curve: Curve.EaseInOut
             })
 
-            // 删除歌单按钮
+            // 删除按钮
             Button() {
-              Text('🗑️')
-                .fontSize(16)
-                .fontColor('#ff4757')
+              Row({ space: 6 }) {
+                Text('🗑️')
+                  .fontSize(16)
+
+                Text('删除')
+                  .fontSize(14)
+                  .fontColor(Color.Red)
+                  .fontWeight(FontWeight.Medium)
+              }
+              .justifyContent(FlexAlign.Center)
             }
-            .width(44)
+            .layoutWeight(1)
             .height(44)
             .backgroundColor(Color.Transparent)
             .borderRadius(22)
             .border({
               width: 1.5,
-              color: '#ff4757'
+              color: Color.Red
             })
             .onClick(() => {
               this.deletePlaylist()
@@ -626,7 +649,7 @@ export struct PlaylistDetailPage {
                 .scale({ x: 1, y: 1 })
               },
               pressed: {
-                .backgroundColor('#ff475715')
+                .backgroundColor('#ff000015')
                 .scale({ x: 0.96, y: 0.96 })
               }
             })
@@ -667,6 +690,43 @@ export struct PlaylistDetailPage {
               .fontSize(14)
               .fontColor($r('app.color.text_color'))
               .opacity(0.6)
+
+            // 添加歌曲按钮
+            Button() {
+              Row({ space: 6 }) {
+                Text('+')
+                  .fontSize(18)
+                  .fontColor(Color.White)
+
+                Text('添加歌曲')
+                  .fontSize(14)
+                  .fontColor(Color.White)
+                  .fontWeight(FontWeight.Medium)
+              }
+              .justifyContent(FlexAlign.Center)
+              .padding({ left: 16, right: 16, top: 8, bottom: 8 })
+            }
+            .height(36)
+            .backgroundColor(this.themeColor)
+            .borderRadius(18)
+            .margin({ left: 12 })
+            .onClick(() => {
+              this.addSongsToPlaylist()
+            })
+            .stateStyles({
+              normal: {
+                .backgroundColor(this.themeColor)
+                .scale({ x: 1, y: 1 })
+              },
+              pressed: {
+                .backgroundColor('#0a59f7cc')
+                .scale({ x: 0.96, y: 0.96 })
+              }
+            })
+            .animation({
+              duration: 150,
+              curve: Curve.EaseInOut
+            })
           }
           .width('100%')
           .padding({ left: 24, right: 24, top: 16, bottom: 12 })
@@ -681,90 +741,74 @@ export struct PlaylistDetailPage {
               ListItem() {
                 Button({ type: ButtonType.Normal, stateEffect: true }) {
                   Row() {
-                    // 音乐图标 - 圆形设计,参考LocalMusic
+                    // 歌曲封面 - 改为圆形
                     Stack() {
-                      Image($r('app.media.music_red'))
-                        .width(40)
-                        .height(40)
-                        .fillColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
-                        .borderRadius('100%')
-                        .clip(true)
+                      Image(song.pixelMapPath || $r('app.media.music_red'))
+                        .width(48)
+                        .height(48)
+                        .borderRadius(24) // 改为圆形
+                        .objectFit(ImageFit.Cover)
                         .interpolation(ImageInterpolation.High)
                         .autoResize(true)
-                        .margin({ left: 20 })
+                        .margin({ left: 16 })
                         .onClick(() => {
                           this.playSong(song, index)
                         })
-
-                      // 播放中动画覆盖层
-                      if (this.isPlaying && this.curIndex === index) {
-                        Text('♪')
-                          .fontSize(16)
-                          .fontColor(Color.White)
-                          .position({ x: 0, y: 0 })
-                          .width(40)
-                          .height(40)
-                          .textAlign(TextAlign.Center)
-                          .animation({
-                            duration: 800,
-                            curve: Curve.EaseInOut,
-                            iterations: -1,
-                            playMode: PlayMode.Alternate
-                          })
-                      }
                     }
-                    .width(60)
+                    .width(64)
 
                     // 歌曲信息 - 参考LocalMusic的布局
                     Column() {
-                      // 歌曲名称
-                      Text(song.name || song.fileName || '')
-                        .fontSize(16)
-                        .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
-                        .fontWeight(this.isPlaying && this.curIndex === index ? FontWeight.Bold : FontWeight.Normal)
-                        .maxLines(1)
-                        .textOverflow({ overflow: TextOverflow.Ellipsis })
-                        .width('100%')
-                        .margin({ top: 8, left: 12 })
+                      Row() {
+                        // 歌曲名称
+                        Text(song.name || song.fileName || '')
+                          .fontSize(16)
+                          .fontColor(this.isPlaying && this.curIndex === index ? this.themeColor : $r('app.color.text_color'))
+                          .fontWeight(this.isPlaying && this.curIndex === index ? FontWeight.Bold : FontWeight.Normal)
+                          .maxLines(1)
+                          .textOverflow({ overflow: TextOverflow.Ellipsis })
+                          .layoutWeight(1)
+                          .margin({ top: 8, left: 12 })
+
+                        // 音质标签
+                        if (song.md5Str) {
+                          Text(song.md5Str?.includes('Lossless') ? '无损' : song.md5Str)
+                            .fontSize(10)
+                            .fontColor($r('app.color.text_color'))
+                            .fontWeight(500)
+                            .padding({ top: 2, right: 6, left: 6, bottom: 2 })
+                            .borderRadius(4)
+                            .margin({ top: 8, left: 8 })
+                            .backgroundColor( '#FFC107')
+                            .visibility(song.md5Str ? Visibility.Visible : Visibility.None)
+                        }
+                      }
+                      .width('100%')
 
                       // 歌手和专辑信息 - 参考LocalMusic的第二行布局
                       Row() {
-                        if (song.artist) {
                           Text(song.artist)
                             .fontSize(13)
-                            .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
+                            .fontColor(this.isPlaying && this.curIndex === index ?this.themeColor : $r('app.color.text_color'))
                             .fontWeight(this.isPlaying && this.curIndex === index ? FontWeight.Medium : FontWeight.Normal)
                             .maxLines(1)
                             .textOverflow({ overflow: TextOverflow.Ellipsis })
                             .layoutWeight(1)
                             .opacity(this.isPlaying && this.curIndex === index ? 1.0 : 0.8)
-                        }
-
-                        if (song.artist && song.album) {
-                          Text('·')
-                            .fontSize(13)
-                            .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
-                            .opacity(this.isPlaying && this.curIndex === index ? 0.8 : 0.6)
-                        }
-
-                        if (song.album) {
                           Text(song.album)
                             .fontSize(13)
-                            .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
+                            .fontColor(this.isPlaying && this.curIndex === index ? this.themeColor  : $r('app.color.text_color'))
                             .fontWeight(this.isPlaying && this.curIndex === index ? FontWeight.Medium : FontWeight.Normal)
                             .maxLines(1)
                             .textOverflow({ overflow: TextOverflow.Ellipsis })
                             .layoutWeight(1)
                             .opacity(this.isPlaying && this.curIndex === index ? 1.0 : 0.8)
-                        }
-
-                        Blank()
 
                         // 时长显示
                         if (song.duration && typeof song.duration === 'number' && song.duration > 0) {
                           Text(this.formatDuration(song.duration))
                             .fontSize(13)
-                            .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
+                            .fontColor(this.isPlaying && this.curIndex === index ? this.themeColor  : $r('app.color.text_color'))
                             .fontWeight(this.isPlaying && this.curIndex === index ? FontWeight.Medium : FontWeight.Normal)
                             .opacity(this.isPlaying && this.curIndex === index ? 1.0 : 0.7)
                             .margin({ right: 20 })
@@ -778,6 +822,7 @@ export struct PlaylistDetailPage {
                     .alignItems(HorizontalAlign.Start)
                     .justifyContent(FlexAlign.Center)
 
+
                     // 更多操作按钮
                     Text('⋯')
                       .fontSize(20)
@@ -817,7 +862,7 @@ export struct PlaylistDetailPage {
                 .backgroundColor(this.isPlaying && this.curIndex === index ? '#f0f8ff' : Color.Transparent)
                 .border({
                   width: { left: this.isPlaying && this.curIndex === index ? 3 : 0 },
-                  color: $r('app.color.theme_color')
+                  color: this.themeColor 
                 })
                 .opacity(this.pageOpacity)
                 .translate({ x: 0, y: this.showContent ? 0 : 20 })
@@ -873,7 +918,7 @@ export struct PlaylistDetailPage {
                   .width(64)
                   .height(64)
                   .opacity(0.6)
-                  .fillColor($r('app.color.theme_color'))
+                  .fillColor(this.themeColor )
               }
               .margin({ bottom: 32 })
 
@@ -911,7 +956,7 @@ export struct PlaylistDetailPage {
               }
               .width(160)
               .height(48)
-              .backgroundColor($r('app.color.theme_color'))
+              .backgroundColor(this.themeColor )
               .borderRadius(24)
               .shadow({
                 radius: 12,
@@ -938,7 +983,7 @@ export struct PlaylistDetailPage {
               })
               .stateStyles({
                 normal: {
-                  .backgroundColor($r('app.color.theme_color'))
+                  .backgroundColor(this.themeColor )
                   .scale({ x: 1, y: 1 })
                 },
                 pressed: {
@@ -989,23 +1034,48 @@ export struct PlaylistDetailPage {
    * 显示更多菜单
    */
   showMoreMenu() {
+    // 简单直接的二选一,避免复杂的多级菜单
     AlertDialog.show({
       title: '歌单操作',
-      message: '',
+      message: '🎵 添加歌曲:向歌单添加新音乐\n✏️ 编辑歌单:修改歌单信息\n\n点击"确定"添加歌曲,点击"取消"编辑歌单',
       primaryButton: {
-        value: '取消',
-        action: () => {}
+        value: '取消 (编辑)',
+        action: () => {
+          this.editPlaylist()
+        }
       },
       secondaryButton: {
-        value: '删除歌单',
-        fontColor: Color.Red,
+        value: '确定 (添加)',
         action: () => {
-          this.deletePlaylist()
+          this.addSongsToPlaylist()
         }
       }
     })
   }
 
+  /**
+   * 添加歌曲到歌单
+   */
+  addSongsToPlaylist() {
+    if (this.playlist) {
+      showAddSongsToPlaylistDialog(
+        this.playlist,
+        async (songs: VideoItem[]) => {
+          // 添加选中的歌曲到歌单
+          const success = await this.playlistTable.addSongsToPlaylist(this.playlistId, songs.map(song => song.filePath))
+
+          if (success) {
+            ToastUtil.showToast(`成功添加 ${songs.length} 首歌曲`)
+            // 重新加载歌单详情
+            this.loadPlaylistDetail()
+          } else {
+            ToastUtil.showToast('添加歌曲失败')
+          }
+        }
+      )
+    }
+  }
+
   /**
    * 显示歌曲菜单
    */

+ 10 - 15
entry/src/main/ets/view/LocalMusic.ets

@@ -189,7 +189,7 @@ export struct LocalMusic {
   @Consume isHistory: boolean
   static readonly HISTORY_MUSIC: string = 'music_historyList';
   private table: MediaTable = new MediaTable(getContext(this))
-  private playlistTable: PlaylistTable = new PlaylistTable(getContext(this))
+  private playlistTable: PlaylistTable | null = null
   @State allPlaylists: Playlist[] = []
   @State isZero: boolean = false
   @State fileList: Array<string> = []
@@ -518,6 +518,9 @@ export struct LocalMusic {
 
   // 组件生命周期
   aboutToAppear() {
+    // 初始化 PlaylistTable
+    this.playlistTable = new PlaylistTable(getContext(this))
+    
     this.sonTwoFingerType = PreferencesUtil.getNumberSync('sonTwoFingerType', 3);
     this.columns = PreferencesUtil.getNumberSync('lastColumns', 2);
     if(PreferencesUtil.getBooleanSync('isFirstTiped',true)){
@@ -760,7 +763,6 @@ export struct LocalMusic {
     const context = getContext(this) as common.UIAbilityContext
     context.getApplicationContext().setColorMode(colorMode)
   }
-
   initSetting() {
     this.sortType = PreferencesUtil.getNumberSync(SettingPage.SORT_TYPE, 4)
     this.isCustomizeBg = PreferencesUtil.getBooleanSync(SettingPage.IS_CUSTOMIZE_BG, false)
@@ -1502,7 +1504,6 @@ export struct LocalMusic {
       }
     })
   }
-
   doSortType(index: number) {
     switch (index) {
       case 0:
@@ -2141,7 +2142,6 @@ export struct LocalMusic {
 
 
   }
-
   build() {
 
     Scroll() {
@@ -2626,10 +2626,10 @@ export struct LocalMusic {
    */
   async loadAllPlaylists() {
     try {
-      this.allPlaylists = await this.playlistTable.queryAllPlaylists()
-      LogUtil.info('Loaded playlists: ' + this.allPlaylists.length)
+      this.allPlaylists = await this.playlistTable?.queryAllPlaylists()!
+      LogUtil.info('heanup Loaded playlists: ' + this.allPlaylists.length)
     } catch (error) {
-      LogUtil.error('Failed to load playlists: ' + error.message)
+      LogUtil.error('heaunp Failed to load playlists: ' + error.message)
     }
   }
 
@@ -2645,7 +2645,7 @@ export struct LocalMusic {
       this.allPlaylists,
       async (playlistId: string) => {
         // 添加歌曲到歌单
-        const success = await this.playlistTable.addSongToPlaylist(playlistId, item.filePath)
+        const success = await this.playlistTable?.addSongToPlaylist(playlistId, item.filePath)
         if (success) {
           ToastUtil.showToast('已添加到歌单')
           // 重新加载歌单列表
@@ -2661,7 +2661,7 @@ export struct LocalMusic {
         // 创建新歌单
         showCreatePlaylistDialog(
           async (name: string, description: string) => {
-            const success = await this.playlistTable.createPlaylist(name, description)
+            const success = await this.playlistTable?.createPlaylist(name, description)
             if (success) {
               ToastUtil.showToast('歌单创建成功')
               // 重新加载歌单列表并打开添加对话框
@@ -2862,9 +2862,7 @@ export struct LocalMusic {
     }
     return true
   }
-
   @State opacityItem: number = 1; // 控制透明度的状态变量
-
   @Builder
   private MusicItem(item: VideoItem, index?: number) {
     Button({ type: ButtonType.Normal, stateEffect: true }) {
@@ -3345,7 +3343,6 @@ export struct LocalMusic {
 
     return this.currentTitleCover
   }
-
   @Builder
   listViewTitle() {
     Column() {
@@ -4144,8 +4141,6 @@ export struct LocalMusic {
 
     })
   }
-
-
   // Grid布局的开始
   private dragRefOffSetX: number = 0;
   private dragRefOffSetY: number = 0;
@@ -13047,7 +13042,6 @@ export struct LocalMusic {
     ToastUtil.showToast('播放失败')
   }
 }
-
   /**
    * 处理简化的歌单播放请求
    */
@@ -13197,6 +13191,7 @@ function customPopupBuilder(dataBu: BubbleBean) {
     color: "#22FFFFFF"
   })
 }
+
 // Function to calculate a hash for the file list
 function simpleHash(fileList: string[]): string {
   let hash = 0;