Browse Source

Merge remote-tracking branch 'origin/feature/歌单' into feature/歌单

# Conflicts:
#	entry/src/main/ets/view/LocalMusic.ets
chendeben 10 months ago
parent
commit
d71f3ea6cb

+ 50 - 0
entry/src/main/ets/common/constants/EventConstants.ets

@@ -0,0 +1,50 @@
+/**
+ * 事件ID常量管理
+ * 统一管理项目中所有的emitter事件ID
+ */
+export class EventConstants {
+  /**
+   * 媒体文件打开事件
+   */
+  // 视频打开广播事件
+  static readonly EVENT_VIDEO_OPEN: number = 1;
+
+  // 音频打开广播事件
+  static readonly EVENT_AUDIO_OPEN: number = 2;
+
+  /**
+   * 文件扫描事件
+   */
+  // 扫描文件更新事件
+  static readonly EVENT_SCAN_UPDATE: number = 101;
+
+  /**
+   * 应用设置事件
+   */
+  // 设置更新事件
+  static readonly EVENT_SETTING_UPDATE: number = 333;
+
+  /**
+   * UI交互事件
+   */
+  // SwipeBack状态更新事件
+  static readonly EVENT_SWIPE_BACK_UPDATE: number = 888;
+
+  /**
+   * 用户相关事件
+   */
+  // 用户状态改变事件(登录/登出)
+  static readonly EVENT_USER_STATE_CHANGE: number = 1001;
+
+  /**
+   * 歌单相关事件
+   */
+  // 歌单刷新事件
+  static readonly EVENT_PLAYLIST_REFRESH: number = 2001;
+
+  // 播放歌单事件
+  static readonly EVENT_PLAYLIST_PLAY: number = 2002;
+
+  // 播放状态变化事件
+  static readonly EVENT_PLAYBACK_STATUS: number = 2003;
+}

+ 4 - 3
entry/src/main/ets/entryability/EntryAbility.ets

@@ -15,6 +15,7 @@ import { AbilityConstant, Want } from '@kit.AbilityKit';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
 import { AppUtil, ArrayUtil, GlobalContext, LogUtil, StrUtil } from '@pura/harmony-utils';
 import { DemoConstants } from './DemoConstants';
+import { EventConstants } from '../common/constants/EventConstants';
 
 import { Utility } from '../common/util/Utility';
 import { WXApi, WXEventHandler } from '../common/util/WXApiWrap';
@@ -142,9 +143,9 @@ export default class EntryAbility extends UIAbility {
                 }
             };
             if(Utility.isMeidaByExtension(uri)){
-                emitter.emit({ eventId: 2 }, eventData); // 发送音频打开广播事件
+                emitter.emit({ eventId: EventConstants.EVENT_AUDIO_OPEN }, eventData); // 发送音频打开广播事件
             }else{
-                emitter.emit({ eventId: 1 }, eventData); // 发送视频打开广播事件
+                emitter.emit({ eventId: EventConstants.EVENT_VIDEO_OPEN }, eventData); // 发送视频打开广播事件
             }
         },300)
 
@@ -342,7 +343,7 @@ export default class EntryAbility extends UIAbility {
     //发送广播通知更新UI
     sendChangeEvent() {
         const eventData: emitter.EventData = {};
-        emitter.emit({ eventId: 333 }, eventData); // 发送广播通知更新doSwipBack
+        emitter.emit({ eventId: EventConstants.EVENT_SETTING_UPDATE }, eventData); // 发送广播通知更新doSwipBack
     }
 
 }

+ 104 - 41
entry/src/main/ets/pages/NewIndex.ets

@@ -1,10 +1,11 @@
-import { AppUtil, DeviceUtil, DisplayUtil, LogUtil, PreferencesUtil, ToastUtil } from '@pura/harmony-utils';
+import { AppUtil, ArrayUtil, DeviceUtil, DisplayUtil, LogUtil, PreferencesUtil, ToastUtil } from '@pura/harmony-utils';
 import TitleBar from '../view/TitleBar';
-import { curves, LengthMetrics, router, SegmentButton, SegmentButtonOptions } from '@kit.ArkUI';
+import { curves, LengthMetrics, router, SegmentButton, SegmentButtonOptions, SymbolGlyphModifier } from '@kit.ArkUI';
 import mainViewModel, { MainViewModel } from '../viewmodel/MainViewModel';
 import ItemData from '../viewmodel/ItemData';
 import type { sysResource } from '../viewmodel/ItemData';
 import { CommonConstants } from '../common/constants/CommonConstants';
+import { EventConstants } from '../common/constants/EventConstants';
 import { VideoItem } from '../viewmodel/VideoItem';
 import ScreenUtil from '../common/util/ScreenUtil';
 import { Utility } from '../common/util/Utility';
@@ -42,6 +43,7 @@ import { ChartsCount } from './ChartsCount';
 import { image } from '@kit.ImageKit';
 import { Playlist } from '../viewmodel/Playlist';
 import PlaylistTable from '../common/util/PlaylistTable';
+import { convertPlaylistSongsToVideoItems } from './PlaylistDetailPage';
 
 // import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 
@@ -55,6 +57,11 @@ const TAG = 'NewIndex'; // 日志标签
 @Entry
 @Component
 struct NewIndex {
+  /** 页面上下文 */
+  context = this.getUIContext().getHostContext() as common.UIAbilityContext
+  @Provide currentSongList: Array<VideoItem> = []//当前歌单
+  @Provide currentSongListName:string = '' //当前歌单名称
+  @Provide  currentSongListID:string='' //当前歌单ID
   @State isDarkMode: boolean = false
   /** 列表滚动器,用于抽屉菜单列表滚动 */
   private scroller: Scroller = new Scroller();
@@ -77,8 +84,6 @@ struct NewIndex {
   @Provide('isZero') isZero: boolean = false;
   /** 是否显示赞助入口 */
   @State isShowSponsorship: boolean = false
-  /** 页面上下文 */
-  context = this.getUIContext().getHostContext() as common.UIAbilityContext
   /** 音乐是否为零状态(预留) */
   @Provide('isMusicZero') isMusicZero: boolean = false;
   @State isShowTitleBar: boolean = true //是否显示分类导航条
@@ -171,7 +176,7 @@ struct NewIndex {
       (this.modeType !== 0 && this.isCanBack)) {
       console.info('onecold 返回键处理逻辑:发送音频广播通知更新列表');
       const eventData: emitter.EventData = {};
-      emitter.emit({ eventId: 888 }, eventData); // 发送音频广播通知更新doSwipBack
+      emitter.emit({ eventId: EventConstants.EVENT_SWIPE_BACK_UPDATE }, eventData); // 发送音频广播通知更新doSwipBack
     }else if(this.mType > 0){
       this.getUIContext()?.animateTo({ duration: 555 }, () => {
         // 动画闭包内控制Image组件的出现和消失
@@ -255,12 +260,12 @@ struct NewIndex {
 
     await UserUtil.fetchUserInfo();
     this.refreshUserInfoState();
-    let changeUserState: emitter.InnerEvent = { eventId: 1001 }
+    let changeUserState: emitter.InnerEvent = { eventId: EventConstants.EVENT_USER_STATE_CHANGE }
     emitter.on(changeUserState, () => {
       this.refreshUserInfoState();
     });
 
-    let eventSetting: emitter.InnerEvent = { eventId: 333 }
+    let eventSetting: emitter.InnerEvent = { eventId: EventConstants.EVENT_SETTING_UPDATE }
     // 监听广播事件(通用设置配置更新)
     emitter.on(eventSetting, (eventData: emitter.EventData) => {
       this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
@@ -283,7 +288,7 @@ struct NewIndex {
     await this.initPlaylistTable();
 
     // 监听歌单刷新事件
-    emitter.on({ eventId: 2001 }, (eventData: emitter.EventData) => {
+    emitter.on({ eventId: EventConstants.EVENT_PLAYLIST_REFRESH }, (eventData: emitter.EventData) => {
       this.loadPlaylistList()
     });
   }
@@ -314,11 +319,11 @@ struct NewIndex {
   aboutToDisappear() {
     console.info('NewIndex aboutToDisappear');
     this.breakpointSystem.unregister();
-    emitter.off(888);
-    emitter.off(1001);
+    emitter.off(EventConstants.EVENT_SWIPE_BACK_UPDATE);
+    emitter.off(EventConstants.EVENT_USER_STATE_CHANGE);
     
     // 监听歌单刷新事件
-    emitter.off( 2001 );
+    emitter.off(EventConstants.EVENT_PLAYLIST_REFRESH);
 
   }
 
@@ -404,23 +409,6 @@ struct NewIndex {
   isHiCar() {
 
     return this.isHiCarStatus&&this.curDisplayIsHiCar;
-    // const hiCarAspectRatios: HiCarAspectRatio[] = [
-    //   { ratio: 800 / 480, name: "800x480" },
-    //   { ratio: 762 / 752, name: "762x752" },
-    //   { ratio: 968 / 1280, name: "968x1280" },
-    //   { ratio: 1200 / 1200, name: "1200x1200" },
-    //   { ratio: 1280 / 720, name: "1280x720" },
-    //   { ratio: 1920 / 1080, name: "1920x1080" }
-    // ];
-    // const currentRatio: number = this.windowWidth / this.windowHeight;
-    // const RATIO_TOLERANCE: number = 0.1; // 宽高比容差
-    //
-    // for (const hiCarRatio of hiCarAspectRatios) {
-    //   if (Math.abs(currentRatio - hiCarRatio.ratio) <= RATIO_TOLERANCE) {
-    //     LogUtil.info(`HiCar detected: ${hiCarRatio.name}, actual: ${this.windowWidth}x${this.windowHeight}`);
-    //     return true;
-    //   }
-    // }
 
     return false;
   }
@@ -1093,16 +1081,91 @@ struct NewIndex {
         }
         .backgroundColor(Color.Transparent)
         .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
-        .onClick(() => {
-          this.openPlaylist(playlist)
+        .onClick(async () => {
+          if (!this.playlistTable) {
+            ToastUtil.showToast('歌单功能初始化中,请稍后再试')
+            return
+          }
+          // 加载歌单歌曲
+          const playlistSongs = await this.playlistTable.queryPlaylistSongs(playlist.id)
+          this.currentSongList = await convertPlaylistSongsToVideoItems(this.context,playlistSongs)
+          this.currentSongListName= playlist.name
+          this.currentSongListID = playlist.id
+          this.modeType = 4//切换到歌单模式
+          this.doShowDrawer()
         })
-        .gesture(LongPressGesture().onAction(() => {
-          this.showPlaylistMenu(playlist)
-        }))
+        .bindContextMenu(this.MenuBuilder(playlist), ResponseType.LongPress,
+          {
+            preview: MenuPreviewMode.IMAGE,
+            previewAnimationOptions: { scale: [0.8, 1.0] },
+          })
+        .bindContextMenu(this.MenuBuilder(playlist), ResponseType.RightClick,
+          {
+            preview: MenuPreviewMode.IMAGE,
+            previewAnimationOptions: { scale: [0.8, 1.0] },
+          })
       }
     })
   }
 
+  @Builder
+  MenuBuilder(playlist: Playlist) {
+    Menu(){
+      MenuItem({
+        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.square_and_pencil')),
+        content: '编辑歌单'
+      })
+        .onClick(async() => {
+          this.openPlaylist(playlist)
+
+        })
+
+      MenuItem({
+        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.trash')),
+        content: '删除歌单'
+      })
+        .onClick(async() => {
+          this.deletePlaylist(playlist)
+
+        })
+    }
+
+  }
+
+  /**
+   * 删除歌单
+   */
+  async deletePlaylist(playlist: Playlist) {
+    if (playlist&&this.playlistTable) {
+      // 显示确认对话框
+      AlertDialog.show({
+        title: '删除歌单',
+        message: `确定要删除歌单"${playlist.name}"吗?此操作不可撤销。`,
+        primaryButton: {
+          value: '取消',
+          action: () => {}
+        },
+        secondaryButton: {
+          value: '删除',
+          fontColor: Color.Red,
+          action: async () => {
+            if (this.playlistTable) {
+              const success = await this.playlistTable.deletePlaylist(playlist.id)
+              if (success) {
+                ToastUtil.showToast('歌单删除成功')
+                // 发送刷新事件
+                emitter.emit({ eventId: EventConstants.EVENT_PLAYLIST_REFRESH }, {})
+              } else {
+                ToastUtil.showToast('歌单删除失败')
+              }
+            }
+
+          }
+        }
+      })
+    }
+  }
+
   /**
    * 显示创建歌单对话框
    */
@@ -1126,7 +1189,7 @@ struct NewIndex {
             // 刷新歌单列表
             await this.loadPlaylistList()
             // 发送歌单刷新事件
-            emitter.emit({ eventId: 2001 }, {})
+            emitter.emit({ eventId: EventConstants.EVENT_PLAYLIST_REFRESH }, {})
           } else {
             ToastUtil.showToast('歌单创建失败')
           }
@@ -1156,13 +1219,6 @@ struct NewIndex {
     }
   }
 
-  /**
-   * 显示歌单菜单
-   */
-  showPlaylistMenu(playlist: Playlist) {
-    // TODO: 显示歌单操作菜单(重命名、删除等)
-    console.info('显示歌单菜单:', playlist.name)
-  }
 
   /**
    * 初始化歌单数据库
@@ -1190,6 +1246,13 @@ struct NewIndex {
         const playlists = await this.playlistTable.queryAllPlaylists()
         this.playlistList = playlists
         console.info(`成功加载 ${playlists.length} 个歌单`)
+        if(ArrayUtil.isNotEmpty(this.playlistList)){
+          const playlistSongs = await this.playlistTable.queryPlaylistSongs(this.playlistList[0].id)
+          this.currentSongList = await convertPlaylistSongsToVideoItems(this.context,playlistSongs)
+          this.currentSongListName= this.playlistList[0].name
+          this.currentSongListID = this.playlistList[0].id
+        }
+
       } else {
         console.warn('歌单表未初始化')
       }

+ 401 - 170
entry/src/main/ets/pages/PlaylistDetailPage.ets

@@ -3,16 +3,19 @@ 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';
+import { EventConstants } from '../common/constants/EventConstants';
+import { common } from '@kit.AbilityKit';
 
 /**
- * 简化的歌单播放事件数据
+ * 歌单播放事件数据
  */
-interface SimplifiedPlaylistEventData {
+interface PlaylistEventData {
   playlistId: string;
   playlistName: string;
   songCount: number;
@@ -27,6 +30,7 @@ interface SimplifiedPlaylistEventData {
 @Entry
 @Component
 export struct PlaylistDetailPage {
+  context = this.getUIContext().getHostContext() as common.UIAbilityContext
   @State playlist: Playlist | null = null
   @State songList: VideoItem[] = []
   @State isLoading: boolean = true
@@ -36,12 +40,17 @@ export struct PlaylistDetailPage {
   @State pageOpacity: number = 0
   @State contentScale: number = 0.95
   @State showContent: boolean = false
+  @State isSortMode: boolean = false // 是否处于排序模式
 
   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']) {
@@ -65,18 +74,21 @@ export struct PlaylistDetailPage {
   async loadPlaylistDetail() {
     try {
       this.isLoading = true
-      
+
       if (!this.playlist) {
         ToastUtil.showToast('歌单不存在')
         router.back()
         return
       }
-      
+
       // 加载歌单歌曲
       const playlistSongs = await this.playlistTable.queryPlaylistSongs(this.playlistId)
-      
+
       // 将 PlaylistSong 转换为 VideoItem
-      this.songList = await this.convertPlaylistSongsToVideoItems(playlistSongs)
+      this.songList = await convertPlaylistSongsToVideoItems(this.context,playlistSongs)
+
+      // 歌单加载完成后,加载当前播放状态
+      this.loadCurrentPlaybackStatus()
     } catch (error) {
       LogUtil.error('heanup 加载歌单详情失败: ' + error)
       ToastUtil.showToast('加载歌单详情失败')
@@ -153,16 +165,36 @@ export struct PlaylistDetailPage {
   setupPlaybackStatusListener() {
     try {
       // 监听播放状态变化事件
-      const eventPlaybackStatus: emitter.InnerEvent = { eventId: 2003 }
+      const eventPlaybackStatus: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYBACK_STATUS }
       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
+          const currentFilePath = data['currentFilePath'] as string
+
+          LogUtil.info(`heanup 原始播放状态: isPlaying=${this.isPlaying}, currentFilePath=${currentFilePath}`)
+
+          // 通过filePath在当前歌单中查找对应的索引
+          if (currentFilePath) {
+            const matchedIndex = this.songList.findIndex(song => song.filePath === currentFilePath)
+            if (matchedIndex !== -1) {
+              this.curIndex = matchedIndex
+              LogUtil.info(`heanup 在歌单中找到匹配的歌曲: ${this.songList[matchedIndex].name}, 索引: ${matchedIndex}`)
+            } else {
+              // 当前播放的歌曲不在本歌单中,重置索引
+              this.curIndex = -1
+              LogUtil.info(`heanup 当前播放的歌曲不在本歌单中: ${currentFilePath}`)
+            }
+          } else {
+            this.curIndex = -1
+            LogUtil.info(`heanup 当前没有播放歌曲`)
+          }
 
-          // 如果当前播放的是这个歌单的歌曲,高亮显示
-          LogUtil.info(`heanup 播放状态更新: isPlaying=${this.isPlaying}, curIndex=${this.curIndex}`)
+          LogUtil.info(`heanup 播放状态更新: isPlaying=${oldIsPlaying}->${this.isPlaying}, curIndex=${oldCurIndex}->${this.curIndex}`)
         }
       })
     } catch (error) {
@@ -175,64 +207,42 @@ export struct PlaylistDetailPage {
    */
   removePlaybackStatusListener() {
     try {
-      emitter.off(2003)
+      emitter.off(EventConstants.EVENT_PLAYBACK_STATUS)
     } catch (error) {
       LogUtil.error('heanup 移除播放状态监听失败: ' + error)
     }
   }
 
   /**
-   * 将 PlaylistSong 转换为 VideoItem
+   * 加载当前播放状态
    */
-  async convertPlaylistSongsToVideoItems(playlistSongs: PlaylistSong[]): Promise<VideoItem[]> {
-    const videoItems: VideoItem[] = []
-
-    LogUtil.info(`heanup 开始转换歌曲,共 ${playlistSongs.length} 首`)
-
-    for (const playlistSong of playlistSongs) {
-      try {
-        LogUtil.info(`heanup 查询歌曲: ${playlistSong.songFilePath}`)
-
-        // 从数据库查询完整的歌曲信息
-        const videoItem = await this.mediaTable.queryVideoByFilePath(playlistSong.songFilePath)
-
-        if (videoItem) {
-          LogUtil.info(`heanup 找到歌曲: ${videoItem.name}`)
-          videoItems.push(videoItem)
+  loadCurrentPlaybackStatus() {
+    try {
+      // 从AppStorage获取当前播放的歌曲
+      const currentSong = AppStorage.get<VideoItem>('currentSong')
+      if (currentSong && currentSong.filePath) {
+        LogUtil.info(`heanup 获取到当前播放歌曲: ${currentSong.name}, filePath: ${currentSong.filePath}`)
+
+        // 在歌单中查找匹配的歌曲
+        const matchedIndex = this.songList.findIndex(song => song.filePath === currentSong.filePath)
+        if (matchedIndex !== -1) {
+          this.curIndex = matchedIndex
+          // 假设如果有currentSong说明正在播放
+          this.isPlaying = true
+          LogUtil.info(`heanup 在歌单中找到当前播放的歌曲: ${this.songList[matchedIndex].name}, 索引: ${matchedIndex}`)
         } else {
-          LogUtil.warn(`heanup 数据库中未找到歌曲: ${playlistSong.songFilePath}`)
-
-          // 如果数据库中没有,创建一个基本的 VideoItem
-          const fileName = playlistSong.songFilePath.split('/').pop() || ''
-          const name = fileName.replace(/\.[^/.]+$/, "") // 移除文件扩展名
-
-          const basicVideoItem = new VideoItem(
-            name, // name
-            Date.now().toString() + Math.random(), // id
-            playlistSong.songFilePath, // filePath
-            0, // type (音乐类型)
-            0, // videoSize
-            playlistSong.addTime, // cTime
-            undefined, // pixelMap
-            undefined, // size
-            undefined, // pixelMapPath
-            undefined, // artist
-            undefined, // album
-            fileName, // fileName
-            undefined // lastPlayed
-          )
-
-          videoItems.push(basicVideoItem)
+          LogUtil.info(`heanup 当前播放的歌曲不在本歌单中`)
         }
-      } catch (error) {
-        LogUtil.error('heanup 转换歌曲失败: ' + error)
+      } else {
+        LogUtil.info(`heanup 当前没有播放歌曲`)
       }
+    } catch (error) {
+      LogUtil.error('heanup 加载当前播放状态失败: ' + error)
     }
-
-    LogUtil.info(`heanup 转换完成,共 ${videoItems.length} 首歌曲`)
-    return videoItems
   }
 
+
+
   /**
    * 播放歌单
    */
@@ -243,12 +253,12 @@ export struct PlaylistDetailPage {
     }
 
     // 发送播放歌单事件
-    const eventPlaylistPlay: emitter.InnerEvent = { eventId: 2002 }
+    const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY }
 
     LogUtil.info(`heanup 准备发送播放事件,playlist: ${this.playlist ? '存在' : '不存在'}, songs: ${this.songList.length}首`)
 
-    // 尝试简化数据结构,只发送必要的信息
-    const simplifiedData: SimplifiedPlaylistEventData = {
+    // 构建歌单播放事件数据
+    const playlistData: PlaylistEventData = {
       playlistId: this.playlist?.id || '',
       playlistName: this.playlist?.name || '',
       songCount: this.songList.length,
@@ -257,10 +267,10 @@ export struct PlaylistDetailPage {
       songFilePaths: this.songList.map(song => song.filePath)
     };
 
-    LogUtil.info(`heanup 发送简化事件数据: ${JSON.stringify(simplifiedData)}`)
+    LogUtil.info(`heanup 发送歌单播放事件数据: ${JSON.stringify(playlistData)}`)
 
     const eventData: emitter.EventData = {
-      data: simplifiedData
+      data: playlistData
     };
 
     emitter.emit(eventPlaylistPlay, eventData)
@@ -280,9 +290,9 @@ export struct PlaylistDetailPage {
     // 检查歌曲文件路径
     LogUtil.info(`heanup 所有歌曲文件路径: ${JSON.stringify(this.songList.map(s => s.filePath))}`)
 
-    // 发送播放歌单事件,使用简化数据结构
-    const eventPlaylistPlay: emitter.InnerEvent = { eventId: 2002 }
-    const simplifiedData: SimplifiedPlaylistEventData = {
+    // 发送播放歌单事件
+    const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY }
+    const playlistData: PlaylistEventData = {
       playlistId: this.playlist?.id || '',
       playlistName: this.playlist?.name || '',
       songCount: this.songList.length,
@@ -292,16 +302,14 @@ export struct PlaylistDetailPage {
     };
 
     LogUtil.info(`heanup 准备发送事件,eventId: ${eventPlaylistPlay.eventId}`)
-    LogUtil.info(`heanup 发送单歌曲播放事件数据: ${JSON.stringify(simplifiedData)}`)
+    LogUtil.info(`heanup 发送歌单播放事件数据: ${JSON.stringify(playlistData)}`)
 
     const eventData: emitter.EventData = {
-      data: simplifiedData
+      data: playlistData
     };
 
     LogUtil.info(`heanup eventData对象: ${JSON.stringify(eventData)}`)
-    LogUtil.info(`heanup 即将调用 emitter.emit`)
     emitter.emit(eventPlaylistPlay, eventData)
-    LogUtil.info(`heanup emitter.emit 调用完成`)
   }
 
   /**
@@ -319,7 +327,7 @@ export struct PlaylistDetailPage {
             if (success) {
               ToastUtil.showToast('歌单更新成功')
               // 发送刷新事件
-              const eventRefresh: emitter.InnerEvent = { eventId: 2001 }
+              const eventRefresh: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_REFRESH }
               emitter.emit(eventRefresh, {})
             } else {
               ToastUtil.showToast('歌单更新失败')
@@ -351,7 +359,7 @@ export struct PlaylistDetailPage {
             if (success) {
               ToastUtil.showToast('歌单删除成功')
               // 发送刷新事件
-              emitter.emit({ eventId: 2001 }, {})
+              emitter.emit({ eventId: EventConstants.EVENT_PLAYLIST_REFRESH }, {})
               router.back()
             } else {
               ToastUtil.showToast('歌单删除失败')
@@ -403,23 +411,32 @@ export struct PlaylistDetailPage {
             .height(24)
             .margin({ left: 12, right: 8 })
             .onClick(() => {
-              router.back()
+              // 如果在排序模式,先退出排序模式
+              if (this.isSortMode) {
+                this.exitSortMode()
+              } else {
+                router.back()
+              }
             })
-          
-          Text(this.playlist?.name || '歌单详情')
+
+          Text(this.isSortMode ? '排序模式' : (this.playlist?.name || '歌单详情'))
             .fontSize(18)
             .fontColor(Color.White)
             .fontWeight(FontWeight.Medium)
             .layoutWeight(1)
             .textAlign(TextAlign.Center)
-          
-          // 更多操作按钮
-          Image($r('app.media.ic_more_vert_black_24dp'))
-            .width(24)
-            .height(24)
+
+          // 排序/完成按钮
+          Text(this.isSortMode ? '完成' : '排序')
+            .fontSize(16)
+            .fontColor(Color.White)
             .margin({ right: 12 })
             .onClick(() => {
-              this.showMoreMenu()
+              if (this.isSortMode) {
+                this.exitSortMode()
+              } else {
+                this.enterSortMode()
+              }
             })
         }
         .height(48)
@@ -434,7 +451,7 @@ export struct PlaylistDetailPage {
           LoadingProgress()
             .width(40)
             .height(40)
-            .color($r('app.color.theme_color'))
+            .color(this.themeColor )
           
           Text('加载中...')
             .fontSize(14)
@@ -519,7 +536,7 @@ export struct PlaylistDetailPage {
           .padding({ right: 24 })
 
           // 操作按钮区域
-          Row({ space: 16 }) {
+          Row({ space: 12 }) {
             // 播放全部按钮 - 参考LocalMusic的按钮样式
             Button() {
               Row({ space: 8 }) {
@@ -535,9 +552,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 +567,7 @@ export struct PlaylistDetailPage {
             })
             .stateStyles({
               normal: {
-                .backgroundColor($r('app.color.theme_color'))
+                .backgroundColor(this.themeColor )
                 .scale({ x: 1, y: 1 })
               },
               pressed: {
@@ -568,22 +585,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 +620,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 +650,7 @@ export struct PlaylistDetailPage {
                 .scale({ x: 1, y: 1 })
               },
               pressed: {
-                .backgroundColor('#ff475715')
+                .backgroundColor('#ff000015')
                 .scale({ x: 0.96, y: 0.96 })
               }
             })
@@ -667,6 +691,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 +742,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,15 +823,43 @@ export struct PlaylistDetailPage {
                     .alignItems(HorizontalAlign.Start)
                     .justifyContent(FlexAlign.Center)
 
-                    // 更多操作按钮
-                    Text('⋯')
-                      .fontSize(20)
-                      .fontColor($r('app.color.text_color'))
-                      .opacity(0.6)
+
+                    // 排序模式下显示上下移动按钮,否则显示更多按钮
+                    if (this.isSortMode) {
+                      Row({ space: 8 }) {
+                        // 上移按钮
+                        Text('▲')
+                          .fontSize(16)
+                          .fontColor(index === 0 ? '#cccccc' : this.themeColor)
+                          .onClick(() => {
+                            if (index > 0) {
+                              this.moveSong(index, index - 1)
+                            }
+                          })
+                          .enabled(index > 0)
+
+                        // 下移按钮
+                        Text('▼')
+                          .fontSize(16)
+                          .fontColor(index === this.songList.length - 1 ? '#cccccc' : this.themeColor)
+                          .onClick(() => {
+                            if (index < this.songList.length - 1) {
+                              this.moveSong(index, index + 1)
+                            }
+                          })
+                          .enabled(index < this.songList.length - 1)
+                      }
                       .margin({ right: 15 })
-                      .onClick(() => {
-                        this.showSongMenu(song)
-                      })
+                    } else {
+                      Text('⋯')
+                        .fontSize(20)
+                        .fontColor($r('app.color.text_color'))
+                        .opacity(0.6)
+                        .margin({ right: 15 })
+                        .onClick(() => {
+                          this.showSongMenu(song)
+                        })
+                    }
                   }
                   .width('100%')
                   .height(70)
@@ -797,12 +870,18 @@ export struct PlaylistDetailPage {
                 .height(70)
                 .width('100%')
                 .onClick(() => {
-                  this.playSong(song, index)
+                  // 排序模式下禁用点击播放
+                  if (!this.isSortMode) {
+                    this.playSong(song, index)
+                  }
                 })
                 .gesture(
                   LongPressGesture()
                     .onAction(() => {
-                      this.showSongMenu(song)
+                      // 排序模式下不显示菜单
+                      if (!this.isSortMode) {
+                        this.showSongMenu(song)
+                      }
                     })
                 )
                 .stateStyles({
@@ -810,14 +889,14 @@ export struct PlaylistDetailPage {
                     .backgroundColor(Color.Transparent)
                   },
                   pressed: {
-                    .backgroundColor('#f1f3f5')
+                    .backgroundColor(this.isSortMode ? Color.Transparent : '#f1f3f5')
                   }
                 })
                 // 当前播放歌曲的背景高亮
                 .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 +952,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 +990,7 @@ export struct PlaylistDetailPage {
               }
               .width(160)
               .height(48)
-              .backgroundColor($r('app.color.theme_color'))
+              .backgroundColor(this.themeColor )
               .borderRadius(24)
               .shadow({
                 radius: 12,
@@ -938,7 +1017,7 @@ export struct PlaylistDetailPage {
               })
               .stateStyles({
                 normal: {
-                  .backgroundColor($r('app.color.theme_color'))
+                  .backgroundColor(this.themeColor )
                   .scale({ x: 1, y: 1 })
                 },
                 pressed: {
@@ -975,6 +1054,7 @@ export struct PlaylistDetailPage {
             .transition(TransitionEffect.scale({ x: 0.95, y: 0.95 }).animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
           }
           .layoutWeight(1)
+          .margin({ left: 16, right: 16 })
           .justifyContent(FlexAlign.Center)
           .padding({ top: 20, bottom: 20 })
         }
@@ -989,23 +1069,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('添加歌曲失败')
+          }
+        }
+      )
+    }
+  }
+
   /**
    * 显示歌曲菜单
    */
@@ -1026,4 +1131,130 @@ export struct PlaylistDetailPage {
       }
     })
   }
+
+  /**
+   * 进入排序模式
+   */
+  enterSortMode() {
+    this.isSortMode = true
+    ToastUtil.showToast('点击上下箭头调整歌曲顺序')
+    LogUtil.info('heanup 进入排序模式')
+  }
+
+  /**
+   * 退出排序模式
+   */
+  exitSortMode() {
+    this.isSortMode = false
+    ToastUtil.showToast('排序已保存')
+    LogUtil.info('heanup 退出排序模式')
+  }
+
+  /**
+   * 移动歌曲位置
+   */
+  async moveSong(fromIndex: number, toIndex: number) {
+    if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 ||
+      fromIndex >= this.songList.length || toIndex >= this.songList.length) {
+      return
+    }
+
+    LogUtil.info(`heanup 移动歌曲: from ${fromIndex} to ${toIndex}`)
+
+    // 1. 在本地数组中移动
+    const movedSong = this.songList.splice(fromIndex, 1)[0]
+    this.songList.splice(toIndex, 0, movedSong)
+
+    // 2. 触发UI更新
+    this.songList = [...this.songList]
+
+    // 3. 更新数据库中的sortOrder
+    await this.updateAllSongsSortOrder()
+  }
+
+  /**
+   * 更新所有歌曲的sortOrder到数据库
+   */
+  async updateAllSongsSortOrder() {
+    try {
+      LogUtil.info('heanup 开始更新所有歌曲的sortOrder')
+
+      for (let i = 0; i < this.songList.length; i++) {
+        const song = this.songList[i]
+        const success = await this.playlistTable.updatePlaylistSongSortOrder(
+          this.playlistId,
+          song.filePath,
+          i
+        )
+
+        if (success) {
+          LogUtil.info(`heanup 更新歌曲[${i}] ${song.name} sortOrder成功`)
+        } else {
+          LogUtil.error(`heanup 更新歌曲[${i}] ${song.name} sortOrder失败`)
+        }
+      }
+
+      LogUtil.info('heanup 所有歌曲sortOrder更新完成')
+    } catch (error) {
+      LogUtil.error('heanup 更新歌曲sortOrder失败: ' + error)
+    }
+  }
+}
+
+/**
+ * 将 PlaylistSong 转换为 VideoItem
+ */
+export async function convertPlaylistSongsToVideoItems(context: Context,playlistSongs: PlaylistSong[]): Promise<VideoItem[]> {
+  const videoItems: VideoItem[] = []
+
+  LogUtil.info(`heanup 开始转换歌曲,共 ${playlistSongs.length} 首`)
+
+  const mediaTable: MediaTable = new MediaTable(context)
+  await new Promise<void>((resolve, reject) => {
+    mediaTable.getRdbStore(context,  (err:Error) => {
+      err ? reject(err) : resolve();
+    });
+  });
+  for (const playlistSong of playlistSongs) {
+    try {
+      LogUtil.info(`heanup 查询歌曲: ${playlistSong.songFilePath}`)
+
+      // 从数据库查询完整的歌曲信息
+      const videoItem = await mediaTable.queryVideoByFilePath(playlistSong.songFilePath)
+
+      if (videoItem) {
+        LogUtil.info(`heanup 找到歌曲: ${videoItem.name}`)
+        videoItems.push(videoItem)
+      } else {
+        LogUtil.warn(`heanup 数据库中未找到歌曲: ${playlistSong.songFilePath}`)
+
+        // 如果数据库中没有,创建一个基本的 VideoItem
+        const fileName = playlistSong.songFilePath.split('/').pop() || ''
+        const name = fileName.replace(/\.[^/.]+$/, "") // 移除文件扩展名
+
+        const basicVideoItem = new VideoItem(
+          name, // name
+          Date.now().toString() + Math.random(), // id
+          playlistSong.songFilePath, // filePath
+          0, // type (音乐类型)
+          0, // videoSize
+          playlistSong.addTime, // cTime
+          undefined, // pixelMap
+          undefined, // size
+          undefined, // pixelMapPath
+          undefined, // artist
+          undefined, // album
+          fileName, // fileName
+          undefined // lastPlayed
+        )
+
+        videoItems.push(basicVideoItem)
+      }
+    } catch (error) {
+      LogUtil.error('heanup 转换歌曲失败: ' + error)
+    }
+  }
+
+  LogUtil.info(`heanup 转换完成,共 ${videoItems.length} 首歌曲`)
+  return videoItems
 }

+ 2 - 1
entry/src/main/ets/pages/ScanFilePage.ets

@@ -10,6 +10,7 @@ import MediaTable from '../common/util/MediaTable'
 import { VideoItem } from '../viewmodel/VideoItem'
 import Logger from '../common/util/Logger'
 import { CommonConstants, STR_LOCK_VIDEO } from '../common/constants/CommonConstants'
+import { EventConstants } from '../common/constants/EventConstants'
 import { BusinessError, emitter } from '@kit.BasicServicesKit'
 import { BreakpointTypeEnum } from '../common/util/BreakpointSystem'
 import { resourceManager } from '@kit.LocalizationKit'
@@ -102,7 +103,7 @@ export struct ScanFilePage{
 
   endScan(isOnekey:boolean){
     const eventData: emitter.EventData = {};
-    emitter.emit({ eventId: 101 }, eventData); // 发送视频打开广播事件
+    emitter.emit({ eventId: EventConstants.EVENT_SCAN_UPDATE }, eventData); // 发送视频打开广播事件
     this.watchStatus(false)
     this.currentFilePath = '扫描文件入库成功!'
     if(isOnekey){

+ 2 - 1
entry/src/main/ets/pages/SettingPage.ets

@@ -1,6 +1,7 @@
 import TitleBar from '../view/TitleBar'
 import { promptAction, router } from '@kit.ArkUI'
 import { CommonConstants } from '../common/constants/CommonConstants'
+import { EventConstants } from '../common/constants/EventConstants'
 import { AppUtil, DeviceUtil, DisplayUtil, FileUtil, MD5, PreferencesUtil, ToastUtil } from '@pura/harmony-utils'
 import { CustomContentDialog } from '@kit.ArkUI'
 import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
@@ -2030,7 +2031,7 @@ export struct SettingPage {
   //发送广播通知更新UI
   sendChangeEvent() {
     const eventData: emitter.EventData = {};
-    emitter.emit({ eventId: 333 }, eventData); // 发送广播通知更新doSwipBack
+    emitter.emit({ eventId: EventConstants.EVENT_SETTING_UPDATE }, eventData); // 发送广播通知更新doSwipBack
   }
 
   goSelectImage() {

+ 4 - 3
entry/src/main/ets/pages/UserCenter.ets

@@ -9,6 +9,7 @@ import { http } from '@kit.NetworkKit';
 import { authentication } from '@kit.AccountKit';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
+import { EventConstants } from '../common/constants/EventConstants';
 import { util } from '@kit.ArkTS';
 import { DialogHelper } from '@pura/harmony-dialog';
 import { Pay } from '@cashier_alipay/cashiersdk';
@@ -219,7 +220,7 @@ export struct UserCenter {
       }
     }
     await this.fetchUserInfo();
-    emitter.emit({ eventId: 1001 }, {})
+    emitter.emit({ eventId: EventConstants.EVENT_USER_STATE_CHANGE }, {})
   }
 
   // 通过华为账号信息换取用户信息和会员状态
@@ -234,7 +235,7 @@ export struct UserCenter {
       }
     }
     await this.fetchUserInfo();
-    emitter.emit({ eventId: 1001 }, {})
+    emitter.emit({ eventId: EventConstants.EVENT_USER_STATE_CHANGE }, {})
   }
 
   @Builder
@@ -562,7 +563,7 @@ export struct UserCenter {
                 this.subscriptionEndDate = '';
                 this.isForever=false;
                 UserUtil.logout();
-                emitter.emit({ eventId: 1001 }, {})
+                emitter.emit({ eventId: EventConstants.EVENT_USER_STATE_CHANGE }, {})
                 ToastUtil.showToast('已退出登录')
               })
           } else {

+ 134 - 228
entry/src/main/ets/view/LocalMusic.ets

@@ -1,7 +1,6 @@
 import TitleBar from './TitleBar'
 import { curves, display, PiPWindow, promptAction, router, SymbolGlyphModifier, window } from '@kit.ArkUI';
 import { VideoItem } from '../viewmodel/VideoItem';
-import {  stringForTime } from '../common/util/CommUtils';
 import { Playlist } from '../viewmodel/Playlist';
 import {  LengthMetrics, SegmentButton,borderRadiuses, SegmentButtonOptions } from '@kit.ArkUI';
 import {
@@ -23,6 +22,7 @@ import {
 import { imagePathToPixelMap } from '../common/util/CommUtils';
 import { unifiedDataChannel, uniformDataStruct, uniformTypeDescriptor } from '@kit.ArkData';
 import { CommonConstants } from '../common/constants/CommonConstants';
+import { EventConstants } from '../common/constants/EventConstants';
 import Logger from '../common/util/Logger';
 import { photoAccessHelper } from '@kit.MediaLibraryKit';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
@@ -94,9 +94,9 @@ import { showCreatePlaylistDialog } from '../dialog/PlaylistDialog';
 const TAG = 'LocalMusic';
 
 /**
- * 简化的歌单播放事件数据
+ * 歌单播放事件数据
  */
-interface SimplifiedPlaylistEventData {
+interface PlaylistEventData {
   playlistId: string;
   playlistName: string;
   songCount: number;
@@ -162,10 +162,9 @@ const ITEM_HEIGHT_BIG: number = 78; // 列表项大高度
 @Preview
 @Component
 export struct LocalMusic {
-  @State isRefreshing: boolean = false;
-  @State maxRefreshingHeight: number = 100.0;
-  private contentNode?: ComponentContent<Object> = undefined;
-  @State ratioL: number = 1;
+  @Consume currentSongList: Array<VideoItem>//当前歌单
+  @Consume currentSongListName:string //当前歌单名称
+  @Consume @Watch('onIDChange') currentSongListID:string //当前歌单ID
   @State mediaKuCount: number = 0;
   @State albumCount: number = 0;
   @State artistCount: number = 0;
@@ -194,7 +193,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> = []
@@ -296,6 +295,17 @@ export struct LocalMusic {
     }
   }
 
+  //歌单id发生变化的时候回调
+  onIDChange(){
+    if(this.modeType==4){
+      this.titleBarModel.setLeftIconMainNoAnimate($r('app.media.menu'))
+      this.updateListData(this.currentSongList, true)
+      this.titleBarModel.setTitleName(this.currentSongListName)
+      this.isShowTitleBar = false
+    }
+
+  }
+
   onModeChange() {
     this.isFavMusic = false
     if (this.modeType === 2 || this.modeType === 3) {
@@ -304,6 +314,7 @@ export struct LocalMusic {
       this.titleBarModel.setRightIcon(($r('app.media.add')))
     }
     LogUtil.info('onecold onModeChange = ' + this.modeType)
+    this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
     switch (this.modeType) {
       case 0:
         this.getSortedFiles(this.currentPath)
@@ -324,6 +335,12 @@ export struct LocalMusic {
         this.updateListData(this.albumList)
         this.titleBarModel.setTitleName(Utility.resourceToString(this.context, $r('app.string.album')))
         break
+      case 4://歌单
+        this.titleBarModel.setLeftIconMainNoAnimate($r('app.media.menu'))
+        this.updateListData(this.currentSongList, true)
+        this.titleBarModel.setTitleName(this.currentSongListName)
+        this.isShowTitleBar = false
+        break
     }
   }
 
@@ -523,6 +540,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)){
@@ -546,7 +566,7 @@ export struct LocalMusic {
 
 
 
-    let eventMusic: emitter.InnerEvent = { eventId: 2 }
+    let eventMusic: emitter.InnerEvent = { eventId: EventConstants.EVENT_AUDIO_OPEN }
     // 监听广播事件(打开其他应用处理)
     emitter.on(eventMusic, (eventData: emitter.EventData) => {
       this.saveVideoDatas([eventData.data?.message], true)
@@ -554,7 +574,7 @@ export struct LocalMusic {
     });
 
     //侧滑广播接收时间
-    let eventUpdateSwipeBack: emitter.InnerEvent = { eventId: 888 }
+    let eventUpdateSwipeBack: emitter.InnerEvent = { eventId: EventConstants.EVENT_SWIPE_BACK_UPDATE }
     // 监听广播事件(打开其他应用处理)
     emitter.on(eventUpdateSwipeBack, (eventData: emitter.EventData) => {
 
@@ -565,14 +585,14 @@ export struct LocalMusic {
     });
 
     //ScanFilePage广播接收时间
-    let eventScanUpdate: emitter.InnerEvent = { eventId: 101 }
+    let eventScanUpdate: emitter.InnerEvent = { eventId: EventConstants.EVENT_SCAN_UPDATE }
     // 监听ScanFilePage广播事件(更新数据库)
     emitter.on(eventScanUpdate, (eventData: emitter.EventData) => {
       this.doUpdateData()
     });
 
     // 监听歌单播放请求事件
-    let eventPlaylistPlay: emitter.InnerEvent = { eventId: 2002 }
+    let eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY }
     emitter.on(eventPlaylistPlay, (eventData: emitter.EventData) => {
       Logger.info('heanup eventPlaylistPlay received - full eventData: ' + JSON.stringify(eventData))
       Logger.info('heanup eventPlaylistPlay received - eventData.data: ' + JSON.stringify(eventData.data))
@@ -584,11 +604,11 @@ export struct LocalMusic {
         return
       }
 
-      // 检查简化数据结构
+      // 检查歌单播放数据结构
       if (data.playlistId && data.songFilePaths && data.startIndex !== undefined) {
-        Logger.info('heanup eventPlaylistPlay: received simplified playlist data')
-        // 手动构建简化数据对象以避免类型转换问题
-        const simplifiedData: SimplifiedPlaylistEventData = {
+        Logger.info('heanup eventPlaylistPlay: 接收到歌单播放数据')
+        // 手动构建数据对象以避免类型转换问题
+        const playlistData: PlaylistEventData = {
           playlistId: data.playlistId as string,
           playlistName: data.playlistName as string,
           songCount: data.songCount as number,
@@ -596,26 +616,19 @@ export struct LocalMusic {
           songFilePaths: data.songFilePaths as string[]
         }
         // 根据文件路径重新构建歌曲列表
-        this.handleSimplifiedPlaylistPlayRequest(
-          simplifiedData.playlistId,
-          simplifiedData.playlistName,
-          simplifiedData.songFilePaths,
-          simplifiedData.startIndex
+        this.handlePlaylistPlayRequest(
+          playlistData.playlistId,
+          playlistData.playlistName,
+          playlistData.songFilePaths,
+          playlistData.startIndex
         )
         return
       }
 
-      // 检查原始数据结构(向后兼容)
-      if (data.playlist && data.songs && data.startIndex !== undefined) {
-        Logger.info('heanup eventPlaylistPlay: received original playlist data')
-        this.handlePlaylistPlayRequest(data.playlist as Playlist, data.songs as VideoItem[], data.startIndex as number)
-        return
-      }
-
-      Logger.error('heanup eventPlaylistPlay: invalid data structure received')
+      Logger.error('heanup eventPlaylistPlay: 接收到无效的数据结构')
     });
 
-    let eventSetting: emitter.InnerEvent = { eventId: 333 }
+    let eventSetting: emitter.InnerEvent = { eventId: EventConstants.EVENT_SETTING_UPDATE }
     // 监听广播事件(通用设置配置更新)
     emitter.on(eventSetting, (eventData: emitter.EventData) => {
       this.doChangeSetting()
@@ -765,7 +778,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)
@@ -896,7 +908,6 @@ export struct LocalMusic {
           this.doUpdateData()
           break;
         case 102: //查询媒体库列表
-          this.isRefreshing = false
           this.mediaKuList = e.data.data
           Logger.info('onecold 查询媒体库列表 this.mediaKuList length= ' + this.mediaKuList.length)
           Utility.doSortListAscending(this.mediaKuList)
@@ -915,7 +926,6 @@ export struct LocalMusic {
           }
           break;
         case 103: //收到查询艺术家列表
-          this.isRefreshing = false
           this.artistMap = e.data.data1;
           this.artistList = e.data.data2;
           PreferencesUtil.putSync('artistCount', this.artistList.length)
@@ -928,12 +938,15 @@ export struct LocalMusic {
           } else {
             PreferencesUtil.putSync('artistList',  JSON.stringify(this.artistList));
           }
-          const mapArray = Array.from(this.artistMap.entries());
-          PreferencesUtil.putSync('artistMap',  JSON.stringify(mapArray));
+          // 处理并缓存前60条艺术家数据
+          const sortedEntries = Array.from(this.artistMap.entries())
+            .sort((a, b) => b[1].length - a[1].length)
+            .slice(0, 60);
+
+          PreferencesUtil.putSync('artistMap',  JSON.stringify(sortedEntries));
           break;
 
         case 104: //收到查询专辑列表
-          this.isRefreshing = false
           this.albumMap = e.data.data1;
 
           this.albumList = e.data.data2
@@ -952,7 +965,9 @@ export struct LocalMusic {
           } else {
             PreferencesUtil.putSync('albumList',  JSON.stringify(this.albumList));
           }
-          const mapArrayAlbum = Array.from(this.albumMap.entries());
+          const mapArrayAlbum = Array.from(this.albumMap.entries())
+            .sort((a, b) => b[1].length - a[1].length)
+            .slice(0, 60);
           PreferencesUtil.putSync('albumMap',  JSON.stringify(mapArrayAlbum));
           break;
 
@@ -1075,10 +1090,10 @@ export struct LocalMusic {
     console.info('LifeCycleComponent aboutToDisappear');
     this.knockController?.immersiveDisableListening();
     this.curIndex = 0
-    emitter.off(2);
-    emitter.off(101);
-    emitter.off(888);
-    emitter.off(333);
+    emitter.off(EventConstants.EVENT_AUDIO_OPEN);
+    emitter.off(EventConstants.EVENT_SCAN_UPDATE);
+    emitter.off(EventConstants.EVENT_SWIPE_BACK_UPDATE);
+    emitter.off(EventConstants.EVENT_SETTING_UPDATE);
     this.mDestroyPage = true;
     this.mIjkMediaPlayer.setScreenOnWhilePlaying(false);
     if (this.CONTROL_PlayStatus != PlayStatus.INIT) {
@@ -1373,7 +1388,7 @@ export struct LocalMusic {
           this.titleBarModel.setTitleName(Utility.resourceToString(this.context, $r('app.string.album')))
           break
         case 4: //同步数据
-          this.asyncCurrentPathData()
+          // this.asyncCurrentPathData()
           break
       }
 
@@ -1510,7 +1525,6 @@ export struct LocalMusic {
       }
     })
   }
-
   doSortType(index: number) {
     switch (index) {
       case 0:
@@ -2149,7 +2163,6 @@ export struct LocalMusic {
 
 
   }
-
   build() {
 
     Scroll() {
@@ -2255,22 +2268,7 @@ export struct LocalMusic {
                 if(this.isGridMusic){
                   this.getGridView()
                 }else {
-                  Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
-                    this.getListView()
-                  }
-                  .pullDownRatio(this.ratioL)
-                  .pullToRefresh(true)
-                  .refreshOffset(0)
-                  .onOffsetChange((offset: number) => {
-                    // 越接近最大距离,下拉跟手系数越小。
-                    this.ratioL = 1 - Math.pow((offset / this.maxRefreshingHeight), 3);
-                  })
-                  .onStateChange((refreshStatus: RefreshStatus) => {
-                    console.info('onecold Refresh onStatueChange state is ' + refreshStatus);
-                  })
-                  .onRefreshing(async () => {
-                    this.refreshCurrent()
-                  })
+                  this.getListView()
                 }
               }
               if (this.modeType == 0) {
@@ -2649,10 +2647,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)
     }
   }
 
@@ -2668,7 +2666,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('已添加到歌单')
           // 重新加载歌单列表
@@ -2684,7 +2682,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('歌单创建成功')
               // 重新加载歌单列表并打开添加对话框
@@ -2885,9 +2883,7 @@ export struct LocalMusic {
     }
     return true
   }
-
   @State opacityItem: number = 1; // 控制透明度的状态变量
-
   @Builder
   private MusicItem(item: VideoItem, index?: number) {
     Button({ type: ButtonType.Normal, stateEffect: true }) {
@@ -3085,7 +3081,7 @@ export struct LocalMusic {
 
   private readonly tabs: string[] = ['文件夹', '媒体库', '艺术家', '专辑']
   @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.tab({
-    buttons: [{ text: '文件夹' }, { text: '媒体库' },{ text: '艺术家' },{ text: '专辑' }],
+    buttons: [{ text: '文件夹' }, { text: '媒体库' },{ text: '艺术家' }],
     direction: Direction.Ltr,
     buttonPadding:{top:12,bottom:12},
     backgroundColor: Color.Transparent,
@@ -3368,20 +3364,6 @@ export struct LocalMusic {
 
     return this.currentTitleCover
   }
-
-  refreshCurrent() {
-    if(this.modeType==0){
-      this.getHistoryList(false)
-      this.asyncCurrentPathOnlyFile()
-    }else if(this.modeType == 1){
-      workerInstance.postMessage({ code: 2, data: this.context,data2:this.packName });
-    }else if(this.modeType == 2){
-      workerInstance.postMessage({ code: 3, data: this.context });
-    }else if(this.modeType == 3){
-      workerInstance.postMessage({ code: 4, data: this.context });
-    }
-  }
-
   @Builder
   listViewTitle() {
     Column() {
@@ -3597,18 +3579,26 @@ export struct LocalMusic {
             this.isSearchMode = true
           })
 
-        // Image($r("app.media.refresh"))
-        //   .fillColor(this.themeColor)
-        //   .width(25)
-        //   .visibility(this.isHistory || this.isCanBack || this.isSearchMode ? Visibility.None : Visibility.Visible)
-        //   .animation({
-        //     duration: 666,
-        //     curve: 'ease-in-out' // 可选动画曲线
-        //   })
-        //   .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
-        //   .onClick(() => {
-        //     this.refreshCurrent()
-        //   })
+        Image($r("app.media.refresh"))
+          .fillColor(this.themeColor)
+          .width(25)
+          .visibility(this.isHistory || this.isCanBack || this.isSearchMode ? Visibility.None : Visibility.Visible)
+          .animation({
+            duration: 666,
+            curve: 'ease-in-out' // 可选动画曲线
+          })
+          .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
+          .onClick(() => {
+            if (this.isFavMusic) {
+              this.getFavList(true)
+            } else {
+              if(this.modeType == 0){
+                this.asyncCurrentPathData()
+              }else if(this.modeType ==1){
+                workerInstance.postMessage({ code: 2, data: this.context,data2:this.packName });
+              }
+            }
+          })
 
         Image($r("app.media.top_rank"))
           .width(25)
@@ -4172,8 +4162,6 @@ export struct LocalMusic {
 
     })
   }
-
-
   // Grid布局的开始
   private dragRefOffSetX: number = 0;
   private dragRefOffSetY: number = 0;
@@ -4794,25 +4782,6 @@ export struct LocalMusic {
     this.pinchValue = 1;
     this.scaleValue = 1;
   }
-
-  // 扫描当前目录下的文件,不扫描子目录
-  asyncCurrentPathOnlyFile(){
-    if(this.modeType==0){
-      const task = new taskpool.Task(scanCurrentDirectoryTask, getContext(this), this.currentPath,
-        this.lockPath,PreferencesUtil.getStringSync('COVER_API',''));
-      taskpool.execute(task, taskpool.Priority.HIGH).then(()=>{
-        if(this.modeType==0){
-          this.getSortedFiles(this.currentPath)
-        }
-        setTimeout(() => {
-          this.isRefreshing = false;
-        }, 100)
-      }).catch((e:object)=>{
-        console.info("task1 catch e: " + e);
-      })
-    }
-  }
-
   @Builder
   getListView() {
 
@@ -10841,8 +10810,8 @@ export struct LocalMusic {
       onClickBItem: async ()=>{
         if(this.mIjkMediaPlayer){
           this.jumpBTime = await this.mIjkMediaPlayer.getCurrentPosition()
-          if(this.jumpBTime<=this.jumpATime||stringForTime(this.jumpATime)==stringForTime(this.jumpBTime)){
-            ToastUtil.showToast('设置B点时间不能小于或等于A点时间')
+          if(this.jumpBTime<=this.jumpATime){
+            ToastUtil.showToast('设置B点时间不能小于A点时间')
             this.jumpBTime = 0
           }
         }
@@ -12079,15 +12048,17 @@ export struct LocalMusic {
 
     // 发送播放状态变化事件给歌单详情页面
     try {
-      const eventPlaybackStatus: emitter.InnerEvent = { eventId: 2003 }
+      const eventPlaybackStatus: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYBACK_STATUS }
+      const currentFilePath = this.currentSong?.filePath || ''
       const eventData: emitter.EventData = {
         data: {
           isPlaying: this.isPlaying,
-          curIndex: this.curIndex
+          curIndex: this.curIndex,
+          currentFilePath: currentFilePath
         }
       };
       emitter.emit(eventPlaybackStatus, eventData)
-      Logger.info(`heanup 发送播放状态变化事件: isPlaying=${this.isPlaying}, curIndex=${this.curIndex}`)
+      Logger.info(`heanup 发送播放状态变化事件: isPlaying=${this.isPlaying}, curIndex=${this.curIndex}, currentFilePath=${currentFilePath}`)
     } catch (error) {
       Logger.error('heanup 发送播放状态变化事件失败: ' + error)
     }
@@ -13071,72 +13042,72 @@ export struct LocalMusic {
   /**
    * 处理歌单播放请求
    */
-  private  handlePlaylistPlayRequest(playlist: Playlist, songs: VideoItem[], startIndex: number) {
-  try {
-    // 1. 替换当前播放列表
-    this.songList = songs
-    this.sonDataSource.pushArrayData(songs)
-
-    // 2. 设置当前播放索引
-    this.curIndex = startIndex
-
-    // 3. 播放指定歌曲
-    if (songs[startIndex]) {
-      this.doPlay(songs[startIndex], startIndex)
-    }
-
-    // 4. 保存播放列表
-    PreferencesUtil.putSync('LastMusicList', this.songList)
-
-    console.info(`heanup 开始播放歌单: ${playlist.name}, 歌曲数量: ${songs.length}, 开始索引: ${startIndex}`)
-  } catch (error) {
-    console.error('heanup 处理歌单播放请求失败:', error)
-    ToastUtil.showToast('播放失败')
-  }
-}
-
-  /**
-   * 处理简化的歌单播放请求
-   */
-  private handleSimplifiedPlaylistPlayRequest(playlistId: string, playlistName: string, songFilePaths: string[], startIndex: number) {
+  private handlePlaylistPlayRequest(playlistId: string, playlistName: string, songFilePaths: string[], startIndex: number) {
     try {
-      Logger.info(`heanup 处理简化歌单播放请求: ${playlistName}, 歌曲数量: ${songFilePaths.length}, 开始索引: ${startIndex}`)
+      Logger.info(`heanup 处理歌单播放请求: ${playlistName}, 歌曲数量: ${songFilePaths.length}, 开始索引: ${startIndex}`)
 
-      // 从数据库重新加载这些歌曲
-      const songs: VideoItem[] = []
+      // 从数据库重新加载这些歌曲,使用索引记录位置以保持顺序
+      const songMap: Map<number, VideoItem> = new Map()
       let completedQueries = 0
 
       Logger.info(`heanup 开始从数据库查询 ${songFilePaths.length} 首歌曲`)
 
-      for (const filePath of songFilePaths) {
-        Logger.info(`heanup 正在查询歌曲: ${filePath}`)
+      // 遍历所有文件路径,使用索引记录位置
+      for (let i = 0; i < songFilePaths.length; i++) {
+        const filePath = songFilePaths[i]
+        const index = i
+        Logger.info(`heanup 正在查询歌曲[${index}]: ${filePath}`)
+
         // 使用已初始化的MediaTable实例从数据库查询歌曲信息
         const queryPromise = this.table.queryVideoByFilePath(filePath)
-        Logger.info(`heanup 创建了查询Promise,开始等待结果: ${filePath}`)
+        Logger.info(`heanup 创建了查询Promise,开始等待结果[${index}]: ${filePath}`)
 
         queryPromise.then((videoItem) => {
           completedQueries++
-          Logger.info(`heanup 查询Promise返回结果: ${filePath}, 找到歌曲: ${videoItem ? videoItem.name : 'null'}`)
+          Logger.info(`heanup 查询Promise返回结果[${index}]: ${filePath}, 找到歌曲: ${videoItem ? videoItem.name : 'null'}`)
 
           if (videoItem) {
-            songs.push(videoItem)
-            Logger.info(`heanup 从数据库找到歌曲: ${videoItem.name} (${completedQueries}/${songFilePaths.length})`)
+            // 使用索引作为key保存到Map中,保持原始顺序
+            songMap.set(index, videoItem)
+            Logger.info(`heanup 从数据库找到歌曲[${index}]: ${videoItem.name} (${completedQueries}/${songFilePaths.length})`)
           } else {
-            Logger.warn(`heanup 数据库中未找到歌曲: ${filePath} (${completedQueries}/${songFilePaths.length})`)
+            Logger.warn(`heanup 数据库中未找到歌曲[${index}]: ${filePath} (${completedQueries}/${songFilePaths.length})`)
           }
 
           // 检查是否所有歌曲都已加载完成
           if (completedQueries === songFilePaths.length) {
-            Logger.info(`heanup 所有数据库查询完成,共找到 ${songs.length} 首歌曲`)
+            Logger.info(`heanup 所有数据库查询完成,共找到 ${songMap.size} 首歌曲`)
+
+            // 按照索引顺序重建歌曲数组
+            const songs: VideoItem[] = []
+            for (let j = 0; j < songFilePaths.length; j++) {
+              const song = songMap.get(j)
+              if (song) {
+                songs.push(song)
+                Logger.info(`heanup 按顺序添加歌曲[${j}]: ${song.name}`)
+              }
+            }
+
+            Logger.info(`heanup 最终歌曲列表顺序: ${songs.map((s, idx) => `[${idx}]${s.name}`).join(', ')}`)
             this.finishLoadingPlaylist(songs, startIndex, playlistName)
           }
         }).catch((error: Error) => {
           completedQueries++
-          Logger.error(`heanup 查询歌曲失败: ${filePath}, 错误: ${error.message} (${completedQueries}/${songFilePaths.length})`)
+          Logger.error(`heanup 查询歌曲失败[${index}]: ${filePath}, 错误: ${error.message} (${completedQueries}/${songFilePaths.length})`)
 
           // 即使出错也要检查是否完成所有查询
           if (completedQueries === songFilePaths.length) {
-            Logger.info(`heanup 所有数据库查询完成(包含错误),共找到 ${songs.length} 首歌曲`)
+            Logger.info(`heanup 所有数据库查询完成(包含错误),共找到 ${songMap.size} 首歌曲`)
+
+            // 按照索引顺序重建歌曲数组
+            const songs: VideoItem[] = []
+            for (let j = 0; j < songFilePaths.length; j++) {
+              const song = songMap.get(j)
+              if (song) {
+                songs.push(song)
+              }
+            }
+
             this.finishLoadingPlaylist(songs, startIndex, playlistName)
           }
         })
@@ -13156,40 +13127,21 @@ export struct LocalMusic {
       ToastUtil.showToast('没有找到可播放的歌曲')
       return
     }
-
-    Logger.info(`heanup 找到 ${songs.length} 首可播放的歌曲`)
-
-    // 1. 替换当前播放列表并更新存储
-    Logger.info(`heanup 更新前 - 当前播放列表长度: ${this.songList.length}`)
     this.songList = songs
-    // 确保存储也更新(@StorageLink会自动同步)
-    Logger.info(`heanup 更新后 - 新播放列表长度: ${this.songList.length}`)
 
-    // 2. 更新数据源
-    Logger.info(`heanup 更新前 - 数据源长度: ${this.sonDataSource.totalCount()}`)
     this.sonDataSource.pushArrayData(songs)
-    Logger.info(`heanup 更新后 - 数据源长度: ${this.sonDataSource.totalCount()}`)
 
-    // 3. 强制触发UI重新渲染
-    Logger.info('heanup 触发UI重新渲染')
     this.sonDataSource.notifyDataReload()
 
-    // 4. 设置当前播放索引
     this.curIndex = startIndex
 
-    // 5. 播放指定歌曲
     if (songs[startIndex]) {
-      Logger.info(`heanup 开始播放歌曲: ${songs[startIndex].name}, 索引: ${startIndex}`)
-      Logger.info(`heanup 播放前 - 播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
       // 确保当前播放的歌曲也更新到存储
       AppStorage.setOrCreate('currentSong', songs[startIndex])
       // 设置isFromSonPlayList=true,避免doPlay方法重新从全局列表构建播放列表
-      Logger.info(`heanup 调用doPlay,参数: 歌曲=${songs[startIndex].name}, 索引=${startIndex}, isFromSonPlayList=true`)
       this.doPlay(songs[startIndex], startIndex, true)
-      Logger.info(`heanup doPlay调用完成`)
     }
 
-    // 5. 保存播放列表
     PreferencesUtil.putSync('LastMusicList', this.songList)
 
     ToastUtil.showToast(`开始播放歌单: ${playlistName}`)
@@ -13244,6 +13196,7 @@ function customPopupBuilder(dataBu: BubbleBean) {
     color: "#22FFFFFF"
   })
 }
+
 // Function to calculate a hash for the file list
 function simpleHash(fileList: string[]): string {
   let hash = 0;
@@ -13344,51 +13297,4 @@ function getFileDirName(filePath: string,rootPath:string): string{
   if(result.startsWith('.'))
     result = result.replace(/\./g, '')
   return result
-}
-
-
-//只扫描当前目录下文件(不扫描子文件夹)的方法
-@Concurrent
-async function scanCurrentDirectoryTask(context: Context, dirPath: string, lockPath: string, cover_api: string) {
-  const table: MediaTable = new MediaTable(context);
-
-  try {
-    // 直接获取当前目录下的文件列表
-    const files = FileUtil.listFileSync(dirPath);
-
-    await Promise.all(files.map(async  (file) => {
-      const fPath = `${dirPath}/${file}`;
-
-      // 跳过目录,只处理文件
-      if (FileUtil.isDirectory(fPath))  {
-        return;
-      }
-
-      // 跳过特定格式文件
-      if (fPath.endsWith('.lrc')  || fPath.endsWith('.srt'))  {
-        return;
-      }
-
-      // 检查是否为媒体文件
-      if (Utility.isMeidaByExtension(fPath))  {
-        let mType = CommonConstants.TYPE_LOCAL;
-        if (fPath.includes(lockPath))  {
-          mType = CommonConstants.TYPE_LOCK;
-        }
-
-        const mediaItem = await Utility.uriGetMusicAssetsFromFile(
-          context, fPath, mType, true
-        );
-
-        table.insert(mediaItem,  (id: number) => {
-          // 插入回调函数
-        }, cover_api);
-      }
-    }));
-  } catch (error) {
-    console.error(' 扫描当前目录失败:', error);
-  }
-}
-
-
-
+}