Kaynağa Gözat

Merge branch 'feature/歌单'

# Conflicts:
#	entry/src/main/ets/view/LocalMusic.ets
chendeben 10 ay önce
ebeveyn
işleme
d245795a23

+ 239 - 0
.cursor/rules/music-player-patterns.mdc

@@ -0,0 +1,239 @@
+---
+description: 音乐播放器开发模式与最佳实践
+globs: ["**/view/**/*.ets", "**/viewmodel/**/*.ets", "**/controller/**/*.ets"]
+alwaysApply: false
+---
+
+# 音乐播放器开发模式与最佳实践
+
+## 播放状态管理
+
+### 播放状态枚举
+使用 `PlayStatus` 枚举管理播放状态:
+```typescript
+import { PlayStatus } from '../common/PlayStatus';
+
+// 在组件中使用
+@State playStatus: PlayStatus = PlayStatus.INIT;
+```
+
+### 状态转换模式
+```typescript
+// 播放/暂停切换
+togglePlay() {
+  if (this.playStatus === PlayStatus.PLAY) {
+    this.pauseMusic();
+    this.playStatus = PlayStatus.PAUSE;
+  } else {
+    this.playMusic();
+    this.playStatus = PlayStatus.PLAY;
+  }
+}
+```
+
+## 音频会话管理
+
+### AvSessionController使用
+```typescript
+import { AvSessionController } from '../controller/AvSessionController';
+
+// 获取控制器实例
+private avSessionController = AvSessionController.getInstance();
+
+// 在页面生命周期中注册/注销
+aboutToAppear() {
+  this.avSessionController.registerSessionListener();
+}
+
+aboutToDisappear() {
+  this.avSessionController.unregisterSessionListener();
+}
+```
+
+## 歌词处理模式
+
+### 歌词解析与显示
+```typescript
+// 使用lib中的LyricHelper
+import { LyricHelper } from '@lib/LyricHelper';
+
+// 解析歌词文件
+LyricHelper.parseLyricFile(lyricPath)
+  .then(lyrics => {
+    this.lyrics = lyrics;
+  })
+  .catch((err: Error) => {
+    Logger.error(`歌词解析失败: ${err.message}`);
+  });
+```
+
+### 歌词同步显示
+```typescript
+// 根据当前播放时间获取对应歌词行
+getCurrentLyricLine(currentTime: number): LyricLine | null {
+  if (!this.lyrics || this.lyrics.length === 0) {
+    return null;
+  }
+  
+  for (let i = 0; i < this.lyrics.length; i++) {
+    if (this.lyrics[i].time > currentTime) {
+      return i > 0 ? this.lyrics[i - 1] : null;
+    }
+  }
+  
+  return this.lyrics[this.lyrics.length - 1];
+}
+```
+
+## 媒体文件处理
+
+### 支持的音频格式
+使用 `CommonConstants.REAL_MUSIC_FORMAT` 检查文件格式:
+```typescript
+import { CommonConstants } from '../common/constants/CommonConstants';
+
+function isMusicFile(fileName: string): boolean {
+  const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
+  return CommonConstants.REAL_MUSIC_FORMAT.includes(ext);
+}
+```
+
+### 媒体扫描与索引
+```typescript
+// 扫描本地音乐文件
+async scanLocalMusic(): Promise<MusicItem[]> {
+  const musicFiles: MusicItem[] = [];
+  const context = getContext(this);
+  
+  // 使用文件系统API扫描
+  // 实现细节取决于具体需求
+  
+  return musicFiles;
+}
+```
+
+## 播放列表管理
+
+### 播放列表模型
+```typescript
+import { Playlist } from '../viewmodel/Playlist';
+
+// 创建播放列表
+const playlist = new Playlist();
+playlist.name = "我的播放列表";
+playlist.songs = [song1, song2, song3];
+
+// 保存到本地存储
+PreferencesUtil.saveObject('playlist_' + playlist.id, playlist);
+```
+
+### 播放模式
+```typescript
+// 播放模式枚举
+enum PlayMode {
+  SEQUENCE,  // 顺序播放
+  LOOP,      // 循环播放
+  RANDOM,    // 随机播放
+  SINGLE     // 单曲循环
+}
+
+// 切换播放模式
+switchPlayMode() {
+  const modes = Object.values(PlayMode);
+  const currentIndex = modes.indexOf(this.playMode);
+  this.playMode = modes[(currentIndex + 1) % modes.length];
+}
+```
+
+## 主题适配
+
+### 主题切换
+```typescript
+import { myTheme } from '../common/AppTheme';
+
+// 在组件中使用主题颜色
+@Builder
+PlayerControl() {
+  Row() {
+    Button('播放')
+      .backgroundColor($r('app.color.brand'))
+      .fontColor($r('app.color.fontOnPrimary'))
+  }
+  .backgroundColor($r('app.color.backgroundPrimary'))
+}
+```
+
+### 动态主题更新
+```typescript
+// 更新主题
+updateTheme(themeIndex: number) {
+  const themeList = [DefaultTheme, TwilightTheme, ForestTheme, CoralTheme, MidnightTheme];
+  AppStorage.SetOrCreate('themeColor', themeList[themeIndex].colors.brand);
+}
+```
+
+## 性能优化
+
+### 图片加载优化
+```typescript
+// 使用缓存和懒加载
+Image(this.coverUrl)
+  .width(50)
+  .height(50)
+  .borderRadius(8)
+  .objectFit(ImageFit.Cover)
+  .alt($r('app.media.default_music_icon'))
+  .onComplete(() => {
+    // 加载完成回调
+  })
+  .onError(() => {
+    // 加载失败回调
+  })
+```
+
+### 列表性能优化
+```typescript
+// 使用LazyForEach和缓存
+LazyForEach(this.musicData, (item: MusicItem, index: number) => {
+  ListItem() {
+    MusicItemComponent({ musicItem: item })
+  }
+}, (item: MusicItem) => item.id.toString())
+```
+
+## 错误处理
+
+### 播放错误处理
+```typescript
+// 播放错误处理
+handlePlaybackError(error: Error) {
+  Logger.error(`播放错误: ${error.message}`);
+  this.playStatus = PlayStatus.INIT;
+  
+  // 显示错误提示
+  ToastUtil.showToast(`播放失败: ${error.message}`);
+}
+```
+
+### 网络请求错误处理
+```typescript
+// API请求错误处理
+fetchLyrics(title: string, artist: string) {
+  const url = `${CommonConstants.LRC_API}?title=${encodeURIComponent(title)}&artist=${encodeURIComponent(artist)}`;
+  
+  fetch(url)
+    .then(response => {
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      }
+      return response.json();
+    })
+    .then(data => {
+      this.processLyrics(data);
+    })
+    .catch((err: Error) => {
+      Logger.error(`获取歌词失败: ${err.message}`);
+      // 使用备用API或显示错误
+    });
+}
+```

+ 331 - 0
.cursor/rules/native-integration.mdc

@@ -0,0 +1,331 @@
+---
+description: 原生模块集成指南
+globs: ["**/cpp/**/*", "**/napi/**/*", "**/ijkplayer/**/*", "**/lib/**/*"]
+alwaysApply: false
+---
+
+# 原生模块集成指南
+
+## ijkplayer播放器集成
+
+### 基本使用
+```typescript
+import { IjkMediaPlayer } from '@ohos/ijkplayer';
+
+// 创建播放器实例
+private player: IjkMediaPlayer = new IjkMediaPlayer();
+
+// 设置数据源
+async setDataSource(path: string) {
+  try {
+    await this.player.setDataSource(path);
+    await this.player.prepare();
+  } catch (err: Error) {
+    Logger.error(`设置数据源失败: ${err.message}`);
+  }
+}
+```
+
+### 播放控制
+```typescript
+// 播放
+async play() {
+  try {
+    await this.player.start();
+    this.playStatus = PlayStatus.PLAY;
+  } catch (err: Error) {
+    Logger.error(`播放失败: ${err.message}`);
+  }
+}
+
+// 暂停
+async pause() {
+  try {
+    await this.player.pause();
+    this.playStatus = PlayStatus.PAUSE;
+  } catch (err: Error) {
+    Logger.error(`暂停失败: ${err.message}`);
+  }
+}
+
+// 停止
+async stop() {
+  try {
+    await this.player.stop();
+    this.playStatus = PlayStatus.STOP;
+  } catch (err: Error) {
+    Logger.error(`停止失败: ${err.message}`);
+  }
+}
+```
+
+### 播放状态监听
+```typescript
+// 设置播放状态监听器
+setupPlayerListener() {
+  this.player.on('stateChange', (state: string) => {
+    switch (state) {
+      case 'prepared':
+        // 准备完成
+        break;
+      case 'playing':
+        this.playStatus = PlayStatus.PLAY;
+        break;
+      case 'paused':
+        this.playStatus = PlayStatus.PAUSE;
+        break;
+      case 'completed':
+        this.playStatus = PlayStatus.DONE;
+        this.playNext(); // 播放下一首
+        break;
+      case 'error':
+        this.handlePlaybackError(new Error('播放器错误'));
+        break;
+    }
+  });
+
+  this.player.on('timeUpdate', (currentTime: number, duration: number) => {
+    this.currentTime = currentTime;
+    this.duration = duration;
+    this.updateProgress();
+  });
+}
+```
+
+### 音频焦点处理
+```typescript
+import { avSession } from '@kit.ArkAVSessionKit';
+
+// 请求音频焦点
+async requestAudioFocus() {
+  try {
+    const audioSession = await avSession.createAVSession(getContext(this), 'audio', 'music');
+    await audioSession.activate();
+    this.audioSession = audioSession;
+  } catch (err: Error) {
+    Logger.error(`获取音频焦点失败: ${err.message}`);
+  }
+}
+
+// 释放音频焦点
+async releaseAudioFocus() {
+  if (this.audioSession) {
+    try {
+      await this.audioSession.deactivate();
+      await this.audioSession.destroy();
+      this.audioSession = null;
+    } catch (err: Error) {
+      Logger.error(`释放音频焦点失败: ${err.message}`);
+    }
+  }
+}
+```
+
+## 歌词库集成
+
+### LyricHelper使用
+```typescript
+import { LyricHelper } from '@lib/LyricHelper';
+
+// 解析歌词文件
+async parseLyricFile(filePath: string): Promise<LyricLine[]> {
+  try {
+    const lyrics = await LyricHelper.parseLyricFile(filePath);
+    return lyrics;
+  } catch (err: Error) {
+    Logger.error(`解析歌词文件失败: ${err.message}`);
+    return [];
+  }
+}
+
+// 解析歌词文本
+parseLyricText(lyricText: string): LyricLine[] {
+  try {
+    return LyricHelper.parseLyricText(lyricText);
+  } catch (err: Error) {
+    Logger.error(`解析歌词文本失败: ${err.message}`);
+    return [];
+  }
+}
+```
+
+### 歌词同步
+```typescript
+// 获取当前时间对应的歌词行
+getCurrentLyric(currentTime: number): LyricLine | null {
+  if (!this.lyrics || this.lyrics.length === 0) {
+    return null;
+  }
+
+  for (let i = 0; i < this.lyrics.length; i++) {
+    if (this.lyrics[i].time > currentTime) {
+      return i > 0 ? this.lyrics[i - 1] : null;
+    }
+  }
+
+  return this.lyrics[this.lyrics.length - 1];
+}
+
+// 获取下一句歌词
+getNextLyric(currentTime: number): LyricLine | null {
+  if (!this.lyrics || this.lyrics.length === 0) {
+    return null;
+  }
+
+  for (let i = 0; i < this.lyrics.length; i++) {
+    if (this.lyrics[i].time > currentTime) {
+      return this.lyrics[i];
+    }
+  }
+
+  return null;
+}
+```
+
+## NAPI开发指南
+
+### 基本NAPI模块结构
+```cpp
+// napi_init.cpp
+#include "napi/native_api.h"
+
+static napi_value Init(napi_env env, napi_value exports) {
+  // 导出函数
+  napi_property_descriptor desc[] = {
+    {"createPlayer", nullptr, CreatePlayer, nullptr, nullptr, nullptr, napi_default, nullptr},
+    {"destroyPlayer", nullptr, DestroyPlayer, nullptr, nullptr, nullptr, napi_default, nullptr},
+  };
+  
+  napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
+  return exports;
+}
+
+static napi_module demoModule = {
+  .nm_version = 1,
+  .nm_flags = 0,
+  .nm_filename = nullptr,
+  .nm_register_func = Init,
+  .nm_modname = "entry",
+  .nm_priv = ((void*)0),
+  .reserved = {0},
+};
+
+extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
+  napi_module_register(&demoModule);
+}
+```
+
+### 异步操作处理
+```cpp
+// 异步操作结构体
+struct AsyncData {
+  napi_async_work work;
+  napi_deferred deferred;
+  napi_ref callback;
+  std::string result;
+  std::string error;
+};
+
+// 异步执行函数
+static void ExecuteCallback(napi_env env, void* data) {
+  AsyncData* asyncData = (AsyncData*)data;
+  
+  try {
+    // 执行耗时操作
+    asyncData->result = performOperation();
+  } catch (const std::exception& e) {
+    asyncData->error = e.what();
+  }
+}
+
+// 完成回调函数
+static void CompleteCallback(napi_env env, napi_status status, void* data) {
+  AsyncData* asyncData = (AsyncData*)data;
+  
+  napi_value callback;
+  napi_get_reference_value(env, asyncData->callback, &callback);
+  
+  napi_value result;
+  if (asyncData->error.empty()) {
+    napi_create_string_utf8(env, asyncData->result.c_str(), NAPI_AUTO_LENGTH, &result);
+    napi_call_function(env, nullptr, callback, 1, &result, nullptr);
+  } else {
+    napi_value error;
+    napi_create_string_utf8(env, asyncData->error.c_str(), NAPI_AUTO_LENGTH, &error);
+    napi_call_function(env, nullptr, callback, 1, &error, nullptr);
+  }
+  
+  // 清理资源
+  napi_delete_async_work(env, asyncData->work);
+  napi_delete_reference(env, asyncData->callback);
+  delete asyncData;
+}
+```
+
+## 性能优化建议
+
+### 播放器优化
+1. 使用对象池管理播放器实例,避免频繁创建和销毁
+2. 预加载下一首歌曲,减少切换歌曲时的延迟
+3. 使用硬件解码加速,降低CPU占用
+4. 合理设置缓冲区大小,平衡播放流畅度和内存占用
+
+### 内存管理
+1. 及时释放不再使用的资源,如播放器实例、音频会话等
+2. 使用弱引用避免循环引用导致的内存泄漏
+3. 监控内存使用情况,及时处理内存警告
+
+### 线程管理
+1. 将耗时操作放在工作线程中执行,避免阻塞UI线程
+2. 使用线程池管理并发任务,避免创建过多线程
+3. 合理使用同步机制,避免死锁和竞态条件
+
+## 错误处理
+
+### 播放器错误处理
+```typescript
+// 播放器错误处理
+handlePlayerError(error: Error) {
+  Logger.error(`播放器错误: ${error.message}`);
+  
+  // 根据错误类型采取不同处理策略
+  if (error.message.includes('网络')) {
+    // 网络错误,尝试重试或使用本地缓存
+    this.retryWithCache();
+  } else if (error.message.includes('解码')) {
+    // 解码错误,尝试使用备用解码器
+    this.switchToBackupDecoder();
+  } else {
+    // 其他错误,显示错误提示并停止播放
+    this.showErrorMessage(error.message);
+    this.stop();
+  }
+}
+```
+
+### NAPI错误处理
+```cpp
+// NAPI错误处理
+static napi_value SomeFunction(napi_env env, napi_callback_info info) {
+  size_t argc = 1;
+  napi_value args[1];
+  napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
+  
+  if (status != napi_ok || argc < 1) {
+    napi_throw_error(env, nullptr, "Invalid arguments");
+    return nullptr;
+  }
+  
+  // 参数验证
+  bool isString;
+  napi_is_string(env, args[0], &isString);
+  if (!isString) {
+    napi_throw_type_error(env, nullptr, "Expected string");
+    return nullptr;
+  }
+  
+  // 执行操作...
+  
+  return result;
+}
+```

+ 79 - 0
.cursor/rules/project-structure.mdc

@@ -0,0 +1,79 @@
+---
+description: TTMusic项目结构与关键文件指南
+globs: ["**/*.ets", "**/*.ts"]
+alwaysApply: true
+---
+
+# TTMusic项目结构与关键文件指南
+
+## 项目概述
+TTMusic是一个基于鸿蒙ArkTS开发的音乐播放器应用,支持本地音乐播放、歌词显示、主题切换等功能。
+
+## 核心目录结构
+
+### 1. 应用入口
+- `entry/src/main/ets/entryability/EntryAbility.ets` - 应用入口点
+- `entry/src/main/ets/MyAbilityStage.ets` - 应用生命周期管理
+
+### 2. 页面组件
+- `entry/src/main/ets/pages/` - 所有页面组件
+  - `MainIndex.ets` - 主页面,包含底部导航栏
+  - `SplashIndex.ets` - 启动页
+  - `SettingPage.ets` - 设置页面
+  - `PlaylistDetailPage.ets` - 播放列表详情页
+  - `UserCenter.ets` - 用户中心页面
+
+### 3. 视图组件
+- `entry/src/main/ets/view/` - 可复用视图组件
+  - 音乐播放控制组件
+  - 歌词显示组件
+  - 列表项组件
+
+### 4. 视图模型
+- `entry/src/main/ets/viewmodel/` - 数据模型与业务逻辑
+  - `MainViewModel.ets` - 主页面数据模型
+  - `ItemData.ets` - 列表项数据模型
+  - `Playlist.ets` - 播放列表模型
+
+### 5. 控制器
+- `entry/src/main/ets/controller/` - 控制器层
+  - `AvSessionController.ets` - 音频会话控制器
+  - `KnockController.ets` - 敲击检测控制器
+
+### 6. 公共资源
+- `entry/src/main/ets/common/` - 公共工具和常量
+  - `AppTheme.ets` - 主题配置
+  - `PlayStatus.ets` - 播放状态枚举
+  - `constants/CommonConstants.ets` - 通用常量
+  - `util/` - 工具类集合
+
+### 7. 对话框
+- `entry/src/main/ets/dialog/` - 对话框组件
+  - `PlaylistDialog.ets` - 播放列表对话框
+  - `UserPrivacyDialog.ets` - 用户隐私对话框
+
+### 8. 原生模块
+- `ijkplayer/` - FFmpeg播放器原生模块
+- `lib/` - 歌词解析库
+
+## 关键文件说明
+
+
+### 常量配置
+`CommonConstants.ets` 包含应用中使用的所有常量:
+- API端点
+- 文件格式支持列表
+- 播放状态枚举
+- 微信支付配置
+
+### 日志系统
+使用 `common/util/Logger.ets` 进行统一日志记录,基于鸿蒙系统的hilog实现。
+
+## 开发注意事项
+
+1. 所有页面组件应使用 `@Entry` 和 `@Component` 装饰器
+2. 状态管理使用 `@State` 和 `@StorageProp` 装饰器
+3. 遵循ArkTS语法限制,特别是避免使用解构赋值和计算属性名
+4. 使用项目定义的常量而非硬编码值
+5. 遵循项目的命名规范和代码风格
+6. 新增的日志都需要带上"heanup"前缀

+ 545 - 0
.cursor/rules/ui-components.mdc

@@ -0,0 +1,545 @@
+---
+description: UI组件开发模式与最佳实践
+globs: ["**/pages/**/*.ets", "**/view/**/*.ets", "**/dialog/**/*.ets"]
+alwaysApply: false
+---
+
+# UI组件开发模式与最佳实践
+
+## 页面组件结构
+
+### 基本页面模板
+```typescript
+import { CommonConstants } from '../common/constants/CommonConstants';
+import Logger from '../common/util/Logger';
+
+@Entry
+@Component
+struct PageName {
+  // 状态变量
+  @State isLoading: boolean = false;
+  @State dataList: Array<ItemType> = [];
+  
+  // 上下文
+  private context = getContext(this);
+  
+  // 生命周期
+  aboutToAppear() {
+    this.initData();
+  }
+  
+  aboutToDisappear() {
+    this.cleanup();
+  }
+  
+  // 初始化数据
+  private initData() {
+    // 初始化逻辑
+  }
+  
+  // 清理资源
+  private cleanup() {
+    // 清理逻辑
+  }
+  
+  // 构建方法
+  build() {
+    Column() {
+      // 页面内容
+    }
+    .width('100%')
+    .height('100%')
+    .backgroundColor($r('app.color.backgroundPrimary'))
+  }
+}
+```
+
+### 导航栏组件
+```typescript
+@Builder
+NavigationArea(title: string, showBack: boolean = true) {
+  Row() {
+    if (showBack) {
+      Image($r('app.media.ic_back'))
+        .width(24)
+        .height(24)
+        .margin({ left: 16 })
+        .onClick(() => {
+          router.back();
+        })
+    }
+    
+    Text(title)
+      .fontSize(18)
+      .fontWeight(FontWeight.Medium)
+      .fontColor($r('app.color.fontPrimary'))
+      .layoutWeight(1)
+      .textAlign(TextAlign.Center)
+      .margin({ right: showBack ? 40 : 16 })
+  }
+  .width('100%')
+  .height(56)
+  .backgroundColor($r('app.color.backgroundPrimary'))
+}
+```
+
+## 列表组件
+
+### 基本列表组件
+```typescript
+@Component
+struct MusicListItem {
+  @Prop musicItem: MusicItem;
+  @Prop isPlaying: boolean = false;
+  private onItemClick?: (item: MusicItem) => void;
+  
+  build() {
+    Row() {
+      Image(this.musicItem.cover || $r('app.media.default_music_icon'))
+        .width(50)
+        .height(50)
+        .borderRadius(8)
+        .objectFit(ImageFit.Cover)
+        .margin({ right: 12 })
+      
+      Column() {
+        Text(this.musicItem.title)
+          .fontSize(16)
+          .fontColor($r('app.color.fontPrimary'))
+          .maxLines(1)
+          .textOverflow({ overflow: TextOverflow.Ellipsis })
+          .width('100%')
+        
+        Text(this.musicItem.artist)
+          .fontSize(14)
+          .fontColor($r('app.color.fontSecondary'))
+          .maxLines(1)
+          .textOverflow({ overflow: TextOverflow.Ellipsis })
+          .width('100%')
+          .margin({ top: 4 })
+      }
+      .layoutWeight(1)
+      .alignItems(HorizontalAlign.Start)
+      
+      if (this.isPlaying) {
+        Image($r('app.media.ic_playing'))
+          .width(24)
+          .height(24)
+          .margin({ left: 12 })
+      }
+    }
+    .width('100%')
+    .height(70)
+    .padding({ horizontal: 16, vertical: 10 })
+    .onClick(() => {
+      if (this.onItemClick) {
+        this.onItemClick(this.musicItem);
+      }
+    })
+  }
+}
+```
+
+### 高性能列表
+```typescript
+@Component
+struct MusicList {
+  @State musicList: MusicItem[] = [];
+  @State currentPlayingId: string = '';
+  
+  build() {
+    List({ space: 1 }) {
+      LazyForEach(new MusicDataSource(this.musicList), (item: MusicItem, index: number) => {
+        ListItem() {
+          MusicListItem({
+            musicItem: item,
+            isPlaying: item.id === this.currentPlayingId,
+            onItemClick: (musicItem: MusicItem) => {
+              this.playMusic(musicItem);
+            }
+          })
+        }
+      }, (item: MusicItem) => item.id)
+    }
+    .width('100%')
+    .layoutWeight(1)
+    .divider({ strokeWidth: 1, color: $r('app.color.compDivider') })
+  }
+  
+  private playMusic(musicItem: MusicItem) {
+    this.currentPlayingId = musicItem.id;
+    // 播放音乐逻辑
+  }
+}
+
+// 数据源类
+class MusicDataSource implements IDataSource {
+  private listeners: DataChangeListener[] = [];
+  private data: MusicItem[] = [];
+  
+  constructor(data: MusicItem[]) {
+    this.data = data;
+  }
+  
+  totalCount(): number {
+    return this.data.length;
+  }
+  
+  getData(index: number): MusicItem {
+    return this.data[index];
+  }
+  
+  registerDataChangeListener(listener: DataChangeListener): void {
+    if (this.listeners.indexOf(listener) < 0) {
+      this.listeners.push(listener);
+    }
+  }
+  
+  unregisterDataChangeListener(listener: DataChangeListener): void {
+    const pos = this.listeners.indexOf(listener);
+    if (pos >= 0) {
+      this.listeners.splice(pos, 1);
+    }
+  }
+  
+  notifyDataReload(): void {
+    this.listeners.forEach(listener => {
+      listener.onDataReloaded();
+    });
+  }
+}
+```
+
+## 播放控制组件
+
+### 播放控制栏
+```typescript
+@Component
+struct PlayerControlBar {
+  @Prop isPlaying: boolean = false;
+  @Prop currentTime: number = 0;
+  @Prop duration: number = 0;
+  private onPlayPause?: () => void;
+  private onPrevious?: () => void;
+  private onNext?: () => void;
+  private onSeek?: (position: number) => void;
+  
+  build() {
+    Column() {
+      // 进度条
+      Row() {
+        Text(this.formatTime(this.currentTime))
+          .fontSize(12)
+          .fontColor($r('app.color.fontSecondary'))
+        
+        Slider({
+          value: this.currentTime,
+          min: 0,
+          max: this.duration || 1,
+          style: SliderStyle.InSet
+        })
+          .layoutWeight(1)
+          .margin({ horizontal: 12 })
+          .trackColor($r('app.color.compBackgroundTertiary'))
+          .selectedColor($r('app.color.brand'))
+          .blockColor($r('app.color.brand'))
+          .onChange((value: number) => {
+            if (this.onSeek) {
+              this.onSeek(value);
+            }
+          })
+        
+        Text(this.formatTime(this.duration))
+          .fontSize(12)
+          .fontColor($r('app.color.fontSecondary'))
+      }
+      .width('100%')
+      .margin({ bottom: 20 })
+      
+      // 控制按钮
+      Row() {
+        Button() {
+          Image($r('app.media.ic_previous'))
+            .width(28)
+            .height(28)
+            .fillColor($r('app.color.iconPrimary'))
+        }
+        .type(ButtonType.Circle)
+        .backgroundColor(Color.Transparent)
+        .width(48)
+        .height(48)
+        .onClick(() => {
+          if (this.onPrevious) {
+            this.onPrevious();
+          }
+        })
+        
+        Button() {
+          Image(this.isPlaying ? $r('app.media.ic_pause') : $r('app.media.ic_play'))
+            .width(36)
+            .height(36)
+            .fillColor($r('app.color.iconOnPrimary'))
+        }
+        .type(ButtonType.Circle)
+        .backgroundColor($r('app.color.brand'))
+        .width(64)
+        .height(64)
+        .margin({ horizontal: 20 })
+        .onClick(() => {
+          if (this.onPlayPause) {
+            this.onPlayPause();
+          }
+        })
+        
+        Button() {
+          Image($r('app.media.ic_next'))
+            .width(28)
+            .height(28)
+            .fillColor($r('app.color.iconPrimary'))
+        }
+        .type(ButtonType.Circle)
+        .backgroundColor(Color.Transparent)
+        .width(48)
+        .height(48)
+        .onClick(() => {
+          if (this.onNext) {
+            this.onNext();
+          }
+        })
+      }
+      .width('100%')
+      .justifyContent(FlexAlign.Center)
+    }
+    .width('100%')
+    .padding({ horizontal: 20, vertical: 16 })
+  }
+  
+  private formatTime(time: number): string {
+    if (isNaN(time) || time < 0) return '00:00';
+    
+    const minutes = Math.floor(time / 60);
+    const seconds = Math.floor(time % 60);
+    return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+  }
+}
+```
+
+## 对话框组件
+
+### 自定义对话框
+```typescript
+@Component
+struct CustomDialog {
+  @Prop title: string = '';
+  @Prop content: string = '';
+  @Prop confirmText: string = '确定';
+  @Prop cancelText: string = '取消';
+  @Prop showCancel: boolean = true;
+  private onConfirm?: () => void;
+  private onCancel?: () => void;
+  
+  build() {
+    Column() {
+      // 标题
+      Text(this.title)
+        .fontSize(18)
+        .fontWeight(FontWeight.Medium)
+        .fontColor($r('app.color.fontPrimary'))
+        .margin({ top: 24, bottom: 12 })
+        .padding({ horizontal: 20 })
+      
+      // 内容
+      Text(this.content)
+        .fontSize(14)
+        .fontColor($r('app.color.fontSecondary'))
+        .textAlign(TextAlign.Center)
+        .padding({ horizontal: 20 })
+        .margin({ bottom: 24 })
+      
+      // 按钮
+      Row() {
+        if (this.showCancel) {
+          Button(this.cancelText)
+            .fontSize(16)
+            .fontColor($r('app.color.fontPrimary'))
+            .backgroundColor(Color.Transparent)
+            .layoutWeight(1)
+            .onClick(() => {
+              if (this.onCancel) {
+                this.onCancel();
+              }
+            })
+        }
+        
+        Button(this.confirmText)
+          .fontSize(16)
+          .fontColor($r('app.color.brand'))
+          .backgroundColor(Color.Transparent)
+          .layoutWeight(1)
+          .onClick(() => {
+            if (this.onConfirm) {
+              this.onConfirm();
+            }
+          })
+      }
+      .width('100%')
+      .height(48)
+    }
+    .backgroundColor($r('app.color.backgroundPrimary'))
+    .borderRadius(12)
+    .width('80%')
+  }
+}
+```
+
+## 主题适配
+
+### 主题感知组件
+```typescript
+@Component
+struct ThemeAwareButton {
+  @Prop text: string = '';
+  @Prop type: 'primary' | 'secondary' = 'primary';
+  private onClick?: () => void;
+  
+  build() {
+    Button(this.text)
+      .fontSize(16)
+      .fontColor(this.type === 'primary' ? 
+        $r('app.color.fontOnPrimary') : 
+        $r('app.color.fontPrimary'))
+      .backgroundColor(this.type === 'primary' ? 
+        $r('app.color.brand') : 
+        $r('app.color.compBackgroundSecondary'))
+      .borderRadius(8)
+      .padding({ horizontal: 20, vertical: 10 })
+      .onClick(() => {
+        if (this.onClick) {
+          this.onClick();
+        }
+      })
+  }
+}
+```
+
+## 动画效果
+
+### 页面转场动画
+```typescript
+// 页面跳转带动画
+router.pushUrl({
+  url: 'pages/DetailPage',
+  params: { id: this.itemId }
+}).then(() => {
+  // 页面跳转成功
+}).catch((err: Error) => {
+  Logger.error(`页面跳转失败: ${err.message}`);
+});
+
+// 在目标页面中
+@Entry
+@Component
+struct DetailPage {
+  // 页面转场动画
+  pageTransition() {
+    PageTransitionEnter({ duration: 300, curve: Curve.EaseInOut })
+      .slide(SlideEffect.Right)
+    
+    PageTransitionExit({ duration: 300, curve: Curve.EaseInOut })
+      .slide(SlideEffect.Left)
+  }
+  
+  build() {
+    // 页面内容
+  }
+}
+```
+
+### 状态变化动画
+```typescript
+@Component
+struct AnimatedButton {
+  @State isPressed: boolean = false;
+  
+  build() {
+    Button('点击我')
+      .scale({ x: this.isPressed ? 0.95 : 1, y: this.isPressed ? 0.95 : 1 })
+      .animation({
+        duration: 100,
+        curve: Curve.EaseInOut
+      })
+      .onTouch((event: TouchEvent) => {
+        if (event.type === TouchType.Down) {
+          this.isPressed = true;
+        } else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
+          this.isPressed = false;
+        }
+      })
+  }
+}
+```
+
+## 响应式布局
+
+### 断点适配
+```typescript
+@Component
+struct ResponsiveLayout {
+  @State currentBreakpoint: string = 'sm';
+  
+  aboutToAppear() {
+    // 监听窗口大小变化
+    window.getLastWindow(getContext(this))
+      .then((windowClass) => {
+        windowClass.on('windowSizeChange', (windowSize) => {
+          this.updateBreakpoint(windowSize.width);
+        });
+        this.updateBreakpoint(windowClass.getWindowProperties().windowRect.width);
+      });
+  }
+  
+  private updateBreakpoint(width: number) {
+    if (width < 600) {
+      this.currentBreakpoint = 'sm';
+    } else if (width < 840) {
+      this.currentBreakpoint = 'md';
+    } else {
+      this.currentBreakpoint = 'lg';
+    }
+  }
+  
+  build() {
+    if (this.currentBreakpoint === 'sm') {
+      // 小屏幕布局
+      this.buildSmallLayout();
+    } else if (this.currentBreakpoint === 'md') {
+      // 中等屏幕布局
+      this.buildMediumLayout();
+    } else {
+      // 大屏幕布局
+      this.buildLargeLayout();
+    }
+  }
+  
+  @Builder
+  buildSmallLayout() {
+    Column() {
+      // 小屏幕布局内容
+    }
+  }
+  
+  @Builder
+  buildMediumLayout() {
+    Row() {
+      // 中等屏幕布局内容
+    }
+  }
+  
+  @Builder
+  buildLargeLayout() {
+    Grid() {
+      // 大屏幕布局内容
+    }
+  }
+}
+```

+ 172 - 0
CLAUDE.md

@@ -0,0 +1,172 @@
+# CLAUDE.md
+always response 中文
+
+本文件为 Claude Code (claude.ai/code) 在此代码库中工作提供指导。
+
+## 项目概述
+
+TTMusic 是基于 OpenHarmony ArkTS 开发的功能丰富的音乐播放器应用,支持本地和网络音频播放,集成了 ijkplayer 进行媒体处理。
+
+## 构建命令
+
+### 依赖管理
+```bash
+# 安装依赖
+ohpm install
+
+# 更新特定依赖
+ohpm update @ohos/ijkplayer
+```
+
+### 运行测试
+当前项目没有正式的单元测试。测试通过手动设备测试和 DevEco Studio 的内置调试工具进行。
+
+## 架构概览
+
+### 模块结构
+```
+TTMusic/
+├── entry/                  # 主应用模块
+│   ├── src/main/ets/
+│   │   ├── pages/         # 应用页面 (SplashIndex, MainIndex 等)
+│   │   ├── view/          # 可复用UI组件 (LocalMusic, TitleBar 等)
+│   │   ├── viewmodel/     # 数据模型和业务逻辑
+│   │   ├── common/        # 工具类、常量和共享代码
+│   │   ├── controller/    # 控制层 (AvSessionController, KnockController)
+│   │   └── dialog/        # 对话框组件
+├── ijkplayer/             # 基于 FFmpeg 的媒体播放器原生模块
+├── lib/                   # 共享库 (歌词解析等)
+└── hvigor/               # 构建配置
+```
+
+### 核心组件
+
+**核心播放器架构:**
+- `LocalMusic.ets` - 主音乐播放器界面和播放控制
+- `IjkMediaPlayer` - 使用 FFmpeg 的原生媒体播放器后端
+- `AvSessionController.ets` - 用于系统集成的音频会话管理
+- `PlayerModel.ets` - 可观察的播放器状态模型
+
+**导航和UI:**
+- `MainIndex.ets` - 主标签导航 (当前已禁用标签,简化版)
+- `SplashIndex.ets` - 应用初始化和加载页面
+- `PlaylistDetailPage.ets` - 播放列表管理和详情视图
+
+**数据管理:**
+- `MediaTable.ets` & `PlaylistTable.ets` - 媒体和播放列表的数据库操作
+- `ConfigManager.ets` - 基于API的远程配置系统
+- `GlobalContext.ets` - 应用级状态管理
+
+### 数据库架构
+使用关系数据库 (RDB) 用于:
+- 媒体文件元数据和索引
+- 播放列表管理
+- 用户偏好设置
+
+### 配置系统
+通过 `ConfigManager.ets` 进行远程配置管理:
+- 从 `https://pay.ss5.xyz/switches/lists` 获取设置
+- 支持 boolean、number、string 和 JSON 类型
+- 与 AppStorage 集成以实现响应式UI更新
+
+## 关键技术模式
+
+### ArkTS 特定注意事项
+- **禁止解构赋值**: 使用传统循环而不是 `for (const [key, value] of Object.entries(obj))`
+- **需要空值安全**: 在对象方法调用前总是检查 null/undefined
+- **禁止计算属性名**: 使用 `obj[key] = value` 而不是 `{[key]: value}`
+- **显式错误类型**: 使用 `catch (e: Error)` 而不是 `catch (e)`
+- **基于Promise的异步**: 数据库操作使用 `.then()/.catch()` 而不是 async/await
+
+### 状态管理
+- `@State` 用于组件本地状态
+- `@StorageProp`/`@StorageLink` 用于 AppStorage 集成
+- `@Observed` 类用于复杂数据模型
+- 通过 `GlobalContext` 单例进行全局状态管理
+
+### 音频播放集成
+```typescript
+// 标准播放器初始化模式
+const player = IjkMediaPlayer.getInstance();
+player.setDataSource(audioUrl);
+player.prepareAsync();
+player.setOnCompletionListener(this.handleCompletion.bind(this));
+```
+
+### 主题系统
+多个内置主题 (默认、暮色、森林、珊瑚、极夜) 支持:
+- 通过 AppStorage 进行动态颜色切换
+- 基于资源的颜色定义 (`$r('app.color.brand')`)
+- 明暗模式支持
+
+## 开发指南
+
+### 文件组织
+- `pages/` 目录中的页面使用 `@Entry` 装饰器
+- `view/` 目录中的可复用组件
+- `viewmodel/` 中的业务逻辑,使用适当的模型类
+- `common/util/` 中按功能组织的工具类
+
+### 代码风格
+- 类名使用 PascalCase (例如 `MediaTable`)
+- 方法名使用 camelCase (例如 `queryByParentPath`)
+- 常量使用 UPPER_SNAKE_CASE (例如 `DB_COLUMNS.FILE_PATH`)
+- 私有属性使用 `_camelCase` 前缀
+
+### 错误处理
+- 在 catch 块中总是使用显式的 Error 类型
+- 使用项目的 Logger 工具记录错误
+- 通过 ToastUtil 显示用户友好的消息
+- 正确处理数据库 Promise 拒绝
+
+### API 集成
+- 使用 NetAxiosUtil 进行 HTTP 请求
+- 通过 ConfigManager 进行远程配置
+- 正确的 JSON 解析和错误处理
+- 在 CommonConstants 中定义的 API 端点
+
+## 常见开发任务
+
+### 添加新音乐格式
+1. 更新 `CommonConstants.REAL_MUSIC_FORMAT` 数组
+2. 确认 ijkplayer 支持该格式
+3. 使用实际媒体文件测试
+
+### 实现新主题
+1. 在 `AppTheme.ets` 中添加颜色定义
+2. 在设置中更新主题选择UI
+3. 在所有使用主题颜色的组件中测试
+
+### 数据库模式更新
+1. 在相应的 Table 类中修改表创建
+2. 在 `onCreate` 回调中添加版本升级逻辑
+3. 处理现有数据的迁移
+
+### 添加新对话框组件
+1. 在 `dialog/` 目录中创建,遵循现有模式
+2. 使用 `@pura/harmony-dialog` 保持样式一致性
+3. 与父页面状态管理集成
+
+## 重要依赖
+
+- `@ohos/ijkplayer` - 媒体播放引擎 (基于FFmpeg)
+- `@pura/harmony-utils` - 工具函数和助手
+- `@pura/harmony-dialog` - 对话框管理系统
+- `@seagazer/cclyric` - 歌词解析和显示
+- `@chinalike/popup` - 弹窗和模态框组件
+
+## 测试和调试
+
+- 使用 DevEco Studio 的内置调试工具
+- 使用 common/util/Logger.ets 中的 `Logger.info()`、`Logger.error()` 记录日志
+- 在实际设备上测试音频功能
+- 通过日志输出检查数据库操作
+
+## 平台特定注意事项
+
+- 需要 OpenHarmony API 12 (5.0.0(12)) 或更高版本
+- 支持手机、平板和 2in1 设备
+- 通过 `audioPlayback` 后台模式启用后台音频播放
+- 在 module.json5 中配置音频/视频文件类型的文件关联
+- 日志都需要加上一个前缀:“heanup”
+- 禁止使用unknown和any类型

+ 0 - 3
entry/src/main/ets/MyAbilityStage.ets

@@ -23,9 +23,6 @@ export default class MyAbilityStage extends AbilityStage {
       const themeMode = AppStorage.get<number>('themeMode') ?? 0;
       const themeMode = AppStorage.get<number>('themeMode') ?? 0;
       if (themeMode === 0) {
       if (themeMode === 0) {
         AppStorage.setOrCreate('currentColorMode', newConfig.colorMode);
         AppStorage.setOrCreate('currentColorMode', newConfig.colorMode);
-        hilog.info(0x0000, 'Heanup', '新colorMode = %{public}s', JSON.stringify(newConfig.colorMode) ?? '');
-      } else {
-        hilog.info(0x0000, 'Heanup', 'themeMode != 0, skip updating colorMode');
       }
       }
     } catch (err) {
     } catch (err) {
       hilog.error(0x0000, 'Heanup', 'Failed to update color mode: %{public}s', err.message);
       hilog.error(0x0000, 'Heanup', 'Failed to update color mode: %{public}s', err.message);

+ 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;
+}

+ 100 - 0
entry/src/main/ets/common/util/MediaTable.ets

@@ -773,6 +773,106 @@ export default class MediaTable {
     });
     });
   }
   }
 
 
+  /**
+   * Query a VideoItem by file path
+   * @param filePath The file path to query
+   * @returns Promise that resolves with the VideoItem or null if not found
+   */
+  public queryVideoByFilePath(filePath: string): Promise<VideoItem | null> {
+    return new Promise((resolve, reject) => {
+      try {
+        // Create query predicates
+        const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+        predicates.equalTo(DB_COLUMNS.FILE_PATH, filePath);
+
+        // Execute the query
+        this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
+          try {
+            if (resultSet.rowCount === 0) {
+              Logger.info(RdbUtils.RDB_TAG, `No record found for filePath: ${filePath}`);
+              resolve(null);
+              return;
+            }
+
+            // Get the first row
+            if (resultSet.goToFirstRow()) {
+              const videoItem = this.buildVideoItem(resultSet);
+              resolve(videoItem);
+            } else {
+              resolve(null);
+            }
+          } catch (err) {
+            Logger.error(RdbUtils.RDB_TAG, `Error querying video by filePath: ${err.message}`);
+            reject(err);
+          } finally {
+            // Ensure the result set is closed
+            resultSet.close();
+          }
+        });
+      } catch (err) {
+        Logger.error(RdbUtils.RDB_TAG, `Error creating query: ${err.message}`);
+        reject(err);
+      }
+    });
+  }
+
+  /**
+   * Query all video items
+   * @returns Promise that resolves with an array of VideoItem objects
+   */
+  public queryAllVideos(): Promise<VideoItem[]> {
+    return new Promise((resolve, reject) => {
+      try {
+        Logger.info(RdbUtils.RDB_TAG, 'queryAllVideos: 开始查询所有音乐文件');
+        
+        // Create query predicates for all records
+        const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+        // Only query music files (type = 0)
+        predicates.equalTo(DB_COLUMNS.TYPE, 0);
+        // Order by name
+        predicates.orderByAsc(DB_COLUMNS.NAME);
+        
+        Logger.info(RdbUtils.RDB_TAG, 'queryAllVideos: 创建查询条件完成');
+
+        // Execute the query
+        this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
+          try {
+            Logger.info(RdbUtils.RDB_TAG, 'queryAllVideos: 查询回调执行');
+            
+            if (resultSet.rowCount === 0) {
+              Logger.info(RdbUtils.RDB_TAG, 'queryAllVideos: 没有找到音乐文件');
+              resolve([]);
+              return;
+            }
+
+            Logger.info(RdbUtils.RDB_TAG, `queryAllVideos: 找到 ${resultSet.rowCount} 条记录`);
+            
+            const items: VideoItem[] = [];
+            
+            // Go to first row
+            if (resultSet.goToFirstRow()) {
+              do {
+                const item = this.buildVideoItem(resultSet);
+                items.push(item);
+              } while (resultSet.goToNextRow());
+            }
+            
+            Logger.info(RdbUtils.RDB_TAG, `queryAllVideos: 解析完成,返回 ${items.length} 个项目`);
+            resolve(items);
+          } catch (err) {
+            Logger.error(RdbUtils.RDB_TAG, `queryAllVideos: 解析结果集出错: ${err.message}`);
+            Logger.error(RdbUtils.RDB_TAG, `queryAllVideos: 错误堆栈: ${err.stack || '无堆栈信息'}`);
+            reject(err);
+          }
+          // 注意:不在这里关闭 resultSet,因为 RdbUtils.query 会在回调函数执行后关闭它
+        });
+      } catch (err) {
+        Logger.error(RdbUtils.RDB_TAG, `queryAllVideos: 创建查询出错: ${err.message}`);
+        Logger.error(RdbUtils.RDB_TAG, `queryAllVideos: 错误堆栈: ${err.stack || '无堆栈信息'}`);
+        reject(err);
+      }
+    });
+  }
 
 
 }
 }
 
 

+ 503 - 0
entry/src/main/ets/common/util/PlaylistTable.ets

@@ -0,0 +1,503 @@
+import { relationalStore } from '@kit.ArkData';
+import { Context } from '@kit.AbilityKit';
+import Logger from './Logger';
+import RdbUtils from './RdbUtils';
+import { Playlist, PlaylistSong } from '../../viewmodel/Playlist';
+
+/**
+ * 歌单数据库操作类
+ */
+export default class PlaylistTable {
+  private context: Context;
+  private rdbStore: relationalStore.RdbStore | null = null;
+  private initPromise: Promise<void>;
+
+  constructor(context: Context) {
+    this.context = context;
+    this.initPromise = this.initRdbStore();
+  }
+
+  /**
+   * 确保数据库已初始化
+   */
+  private async ensureInitialized(): Promise<void> {
+    await this.initPromise;
+  }
+
+  /**
+   * 初始化数据库
+   */
+  private async initRdbStore(): Promise<void> {
+    try {
+      const config: relationalStore.StoreConfig = {
+        name: 'PlaylistStore.db',
+        securityLevel: relationalStore.SecurityLevel.S1  // 使用标准安全级别
+      };
+
+      this.rdbStore = await relationalStore.getRdbStore(this.context, config);
+
+      // 创建表
+      await this.createTables();
+
+      Logger.info('heanup PlaylistTable', '数据库初始化成功');
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `初始化数据库失败: ${error.message}`);
+    }
+  }
+
+  /**
+   * 创建表
+   */
+  private async createTables(): Promise<void> {
+    if (!this.rdbStore) {
+      return;
+    }
+
+    try {
+      // 创建歌单表
+      await this.rdbStore.executeSql(RdbUtils.PLAYLIST_TABLE.sqlCreate);
+      
+      // 创建歌单歌曲关联表
+      await this.rdbStore.executeSql(RdbUtils.PLAYLIST_SONG_TABLE.sqlCreate);
+      
+      Logger.info('heanup PlaylistTable', '数据库表创建成功');
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `创建表失败: ${error.message}`);
+    }
+  }
+
+  /**
+   * 生成唯一ID
+   */
+  private generateId(): string {
+    return Date.now().toString() + Math.random().toString(36).substr(2, 9);
+  }
+
+  /**
+   * 创建歌单
+   */
+  async createPlaylist(name: string, description?: string, coverPath?: string): Promise<boolean> {
+    await this.ensureInitialized();
+
+    if (!this.rdbStore) {
+      Logger.error('heanup PlaylistTable', '数据库未初始化');
+      return false;
+    }
+
+    try {
+      const id = this.generateId();
+      const now = new Date().toISOString();
+      const sql = 'INSERT INTO playlistTable (id, name, coverPath, description, createTime, updateTime, songCount, sortOrder) VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
+      const params = [id, name, coverPath || null, description || null, now, now, 0, 0];
+
+      await this.rdbStore.executeSql(sql, params);
+      Logger.info('heanup PlaylistTable', `歌单创建成功: ${name}`);
+      return true;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `创建歌单失败: ${error.message}`);
+      return false;
+    }
+  }
+
+  /**
+   * 删除歌单及其所有歌曲
+   */
+  async deletePlaylist(playlistId: string): Promise<boolean> {
+    if (!this.rdbStore) {
+      return false;
+    }
+
+    try {
+      // 先删除歌单歌曲关联
+      const deleteSongsSql = 'DELETE FROM playlistSongTable WHERE playlistId = ?';
+      await this.rdbStore.executeSql(deleteSongsSql, [playlistId]);
+      
+      // 再删除歌单
+      const deletePlaylistSql = 'DELETE FROM playlistTable WHERE id = ?';
+      await this.rdbStore.executeSql(deletePlaylistSql, [playlistId]);
+      
+      Logger.info('heanup PlaylistTable', `歌单删除成功: ${playlistId}`);
+      return true;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `删除歌单失败: ${error.message}`);
+      return false;
+    }
+  }
+
+  /**
+   * 更新歌单信息
+   */
+  async updatePlaylist(playlistId: string, name?: string, description?: string, coverPath?: string): Promise<boolean> {
+    if (!this.rdbStore) {
+      return false;
+    }
+
+    try {
+      const updateTime = new Date().toISOString();
+      const updates: string[] = [];
+      const params: (string | number | null)[] = [];
+
+      if (name !== undefined) {
+        updates.push('name = ?');
+        params.push(name);
+      }
+      if (description !== undefined) {
+        updates.push('description = ?');
+        params.push(description);
+      }
+      if (coverPath !== undefined) {
+        updates.push('coverPath = ?');
+        params.push(coverPath);
+      }
+      
+      updates.push('updateTime = ?');
+      params.push(updateTime);
+      params.push(playlistId);
+
+      const sql = `UPDATE playlistTable SET ${updates.join(', ')} WHERE id = ?`;
+      await this.rdbStore.executeSql(sql, params);
+      
+      Logger.info('heanup PlaylistTable', `歌单更新成功: ${playlistId}`);
+      return true;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `更新歌单失败: ${error.message}`);
+      return false;
+    }
+  }
+
+  /**
+   * 查询所有歌单
+   */
+  async queryAllPlaylists(): Promise<Playlist[]> {
+    await this.ensureInitialized();
+
+    if (!this.rdbStore) {
+      Logger.error('heanup PlaylistTable', '数据库未初始化');
+      return [];
+    }
+
+    try {
+      const sql = 'SELECT * FROM playlistTable ORDER BY sortOrder ASC, createTime DESC';
+      Logger.info('heanup PlaylistTable', '开始查询所有歌单');
+      const resultSet = await this.rdbStore.querySql(sql);
+      
+      const playlists: Playlist[] = [];
+      if (resultSet.goToFirstRow()) {
+        do {
+          const playlist = new Playlist(
+            resultSet.getString(resultSet.getColumnIndex('id')),
+            resultSet.getString(resultSet.getColumnIndex('name')),
+            resultSet.getString(resultSet.getColumnIndex('createTime')),
+            resultSet.getString(resultSet.getColumnIndex('updateTime')),
+            resultSet.getLong(resultSet.getColumnIndex('songCount')),
+            resultSet.getLong(resultSet.getColumnIndex('sortOrder')),
+            resultSet.getString(resultSet.getColumnIndex('coverPath')),
+            resultSet.getString(resultSet.getColumnIndex('description'))
+          );
+          playlists.push(playlist);
+        } while (resultSet.goToNextRow());
+      }
+      
+      resultSet.close();
+      Logger.info('heanup PlaylistTable', `查询到 ${playlists.length} 个歌单`);
+      return playlists;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `查询歌单失败: ${error.message}`);
+      return [];
+    }
+  }
+
+  /**
+   * 根据ID查询歌单
+   */
+  async queryPlaylistById(playlistId: string): Promise<Playlist | null> {
+    if (!this.rdbStore) {
+      return null;
+    }
+
+    try {
+      const sql = 'SELECT * FROM playlistTable WHERE id = ?';
+      const resultSet = await this.rdbStore.querySql(sql, [playlistId]);
+      
+      if (resultSet.goToFirstRow()) {
+        const playlist = new Playlist(
+          resultSet.getString(resultSet.getColumnIndex('id')),
+          resultSet.getString(resultSet.getColumnIndex('name')),
+          resultSet.getString(resultSet.getColumnIndex('createTime')),
+          resultSet.getString(resultSet.getColumnIndex('updateTime')),
+          resultSet.getLong(resultSet.getColumnIndex('songCount')),
+          resultSet.getLong(resultSet.getColumnIndex('sortOrder')),
+          resultSet.getString(resultSet.getColumnIndex('coverPath')),
+          resultSet.getString(resultSet.getColumnIndex('description'))
+        );
+        resultSet.close();
+        return playlist;
+      }
+      
+      resultSet.close();
+      return null;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `查询歌单失败: ${error.message}`);
+      return null;
+    }
+  }
+
+  /**
+   * 添加歌曲到歌单
+   */
+  async addSongToPlaylist(playlistId: string, songFilePath: string): Promise<boolean> {
+    await this.ensureInitialized();
+
+    if (!this.rdbStore) {
+      Logger.error('heanup PlaylistTable', '数据库未初始化');
+      return false;
+    }
+
+    try {
+      Logger.info('heanup PlaylistTable', `开始添加歌曲到歌单: playlistId=${playlistId}, songFilePath=${songFilePath}`);
+
+      // 检查歌曲是否已在歌单中
+      const isInPlaylist = await this.isSongInPlaylist(playlistId, songFilePath);
+      if (isInPlaylist) {
+        Logger.info('heanup PlaylistTable', '歌曲已在歌单中');
+        return false;
+      }
+
+      const id = this.generateId();
+      const addTime = new Date().toISOString();
+      const sql = 'INSERT INTO playlistSongTable (id, playlistId, songFilePath, addTime, sortOrder) VALUES (?, ?, ?, ?, ?)';
+      const params = [id, playlistId, songFilePath, addTime, 0];
+
+      Logger.info('heanup PlaylistTable', `执行SQL插入: ${sql}`);
+      await this.rdbStore.executeSql(sql, params);
+      Logger.info('heanup PlaylistTable', '歌曲插入成功');
+
+      // 更新歌单歌曲数量
+      Logger.info('heanup PlaylistTable', '开始更新歌单歌曲数量');
+      await this.updatePlaylistSongCount(playlistId);
+
+      Logger.info('heanup PlaylistTable', `歌曲添加到歌单成功: ${songFilePath}`);
+      return true;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `添加歌曲到歌单失败: ${error.message}`);
+      return false;
+    }
+  }
+
+  /**
+   * 批量添加歌曲到歌单
+   */
+  async addSongsToPlaylist(playlistId: string, songFilePaths: string[]): Promise<boolean> {
+    await this.ensureInitialized();
+
+    if (!this.rdbStore) {
+      Logger.error('heanup PlaylistTable', '数据库未初始化');
+      return false;
+    }
+
+    if (!songFilePaths || songFilePaths.length === 0) {
+      Logger.error('heanup PlaylistTable', '歌曲路径列表为空');
+      return false;
+    }
+
+    try {
+      Logger.info('heanup PlaylistTable', `开始批量添加歌曲到歌单: playlistId=${playlistId}, 歌曲数量=${songFilePaths.length}`);
+
+      let successCount = 0;
+      for (const songFilePath of songFilePaths) {
+        try {
+          // 检查歌曲是否已在歌单中
+          const isInPlaylist = await this.isSongInPlaylist(playlistId, songFilePath);
+          if (isInPlaylist) {
+            Logger.info('heanup PlaylistTable', `歌曲已在歌单中,跳过: ${songFilePath}`);
+            continue;
+          }
+
+          const id = this.generateId();
+          const addTime = new Date().toISOString();
+          const sql = 'INSERT INTO playlistSongTable (id, playlistId, songFilePath, addTime, sortOrder) VALUES (?, ?, ?, ?, ?)';
+          const params = [id, playlistId, songFilePath, addTime, 0];
+
+          await this.rdbStore.executeSql(sql, params);
+          successCount++;
+          Logger.info('heanup PlaylistTable', `成功添加歌曲: ${songFilePath}`);
+        } catch (error) {
+          Logger.error('heanup PlaylistTable', `添加歌曲失败: ${songFilePath}, 错误: ${error.message}`);
+        }
+      }
+
+      // 更新歌单歌曲数量
+      Logger.info('heanup PlaylistTable', '开始更新歌单歌曲数量');
+      await this.updatePlaylistSongCount(playlistId);
+
+      Logger.info('heanup PlaylistTable', `批量添加歌曲完成,成功添加 ${successCount} 首,共 ${songFilePaths.length} 首`);
+      return successCount > 0;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `批量添加歌曲到歌单失败: ${error.message}`);
+      return false;
+    }
+  }
+
+  /**
+   * 从歌单中移除歌曲
+   */
+  async removeSongFromPlaylist(playlistId: string, songFilePath: string): Promise<boolean> {
+    if (!this.rdbStore) {
+      return false;
+    }
+
+    try {
+      const sql = 'DELETE FROM playlistSongTable WHERE playlistId = ? AND songFilePath = ?';
+      await this.rdbStore.executeSql(sql, [playlistId, songFilePath]);
+      
+      // 更新歌单歌曲数量
+      await this.updatePlaylistSongCount(playlistId);
+      
+      Logger.info('heanup PlaylistTable', `歌曲从歌单移除成功: ${songFilePath}`);
+      return true;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `从歌单移除歌曲失败: ${error.message}`);
+      return false;
+    }
+  }
+
+  /**
+   * 查询歌单中的歌曲
+   */
+  async queryPlaylistSongs(playlistId: string): Promise<PlaylistSong[]> {
+    await this.ensureInitialized();
+
+    if (!this.rdbStore) {
+      Logger.error('heanup PlaylistTable', '数据库未初始化');
+      return [];
+    }
+
+    try {
+      const sql = 'SELECT * FROM playlistSongTable WHERE playlistId = ? ORDER BY sortOrder ASC, addTime ASC';
+      Logger.info('heanup PlaylistTable', `queryPlaylistSongs: 开始查询歌单歌曲, playlistId=${playlistId}`);
+      Logger.info('heanup PlaylistTable', `queryPlaylistSongs: SQL=${sql}`);
+      
+      const resultSet = await this.rdbStore.querySql(sql, [playlistId]);
+      Logger.info('heanup PlaylistTable', `queryPlaylistSongs: 查询执行完成, rowCount=${resultSet.rowCount}`);
+      
+      const songs: PlaylistSong[] = [];
+      if (resultSet.goToFirstRow()) {
+        Logger.info('heanup PlaylistTable', 'queryPlaylistSongs: 移动到第一行成功');
+        do {
+          const song = new PlaylistSong(
+            resultSet.getString(resultSet.getColumnIndex('id')),
+            resultSet.getString(resultSet.getColumnIndex('playlistId')),
+            resultSet.getString(resultSet.getColumnIndex('songFilePath')),
+            resultSet.getString(resultSet.getColumnIndex('addTime')),
+            resultSet.getLong(resultSet.getColumnIndex('sortOrder'))
+          );
+          songs.push(song);
+          Logger.info('heanup PlaylistTable', `queryPlaylistSongs: 添加歌曲, songFilePath=${song.songFilePath}`);
+        } while (resultSet.goToNextRow());
+      } else {
+        Logger.info('heanup PlaylistTable', 'queryPlaylistSongs: 没有数据,无法移动到第一行');
+      }
+      
+      resultSet.close();
+      Logger.info('heanup PlaylistTable', `queryPlaylistSongs: 查询到 ${songs.length} 首歌曲`);
+      return songs;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `queryPlaylistSongs: 查询歌单歌曲失败: ${error.message}`);
+      Logger.error('heanup PlaylistTable', `queryPlaylistSongs: 错误堆栈: ${error.stack || '无堆栈信息'}`);
+      return [];
+    }
+  }
+
+  /**
+   * 检查歌曲是否在歌单中
+   */
+  async isSongInPlaylist(playlistId: string, songFilePath: string): Promise<boolean> {
+    if (!this.rdbStore) {
+      return false;
+    }
+
+    try {
+      const sql = 'SELECT COUNT(*) as count FROM playlistSongTable WHERE playlistId = ? AND songFilePath = ?';
+      const resultSet = await this.rdbStore.querySql(sql, [playlistId, songFilePath]);
+      
+      let isInPlaylist = false;
+      if (resultSet.goToFirstRow()) {
+        const count = resultSet.getLong(resultSet.getColumnIndex('count'));
+        isInPlaylist = count > 0;
+      }
+      
+      resultSet.close();
+      return isInPlaylist;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `检查歌曲是否在歌单中失败: ${error.message}`);
+      return false;
+    }
+  }
+
+  /**
+   * 更新歌单歌曲数量
+   */
+  private async updatePlaylistSongCount(playlistId: string): Promise<void> {
+    if (!this.rdbStore) {
+      return;
+    }
+
+    try {
+      const countSql = 'SELECT COUNT(*) as count FROM playlistSongTable WHERE playlistId = ?';
+      const resultSet = await this.rdbStore.querySql(countSql, [playlistId]);
+      
+      let count = 0;
+      if (resultSet.goToFirstRow()) {
+        count = resultSet.getLong(resultSet.getColumnIndex('count'));
+      }
+      resultSet.close();
+      
+      const updateSql = 'UPDATE playlistTable SET songCount = ?, updateTime = ? WHERE id = ?';
+      const updateTime = new Date().toISOString();
+      await this.rdbStore.executeSql(updateSql, [count, updateTime, playlistId]);
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `更新歌单歌曲数量失败: ${error.message}`);
+    }
+  }
+
+  /**
+   * 更新歌单排序
+   */
+  async updatePlaylistSortOrder(playlistId: string, sortOrder: number): Promise<boolean> {
+    if (!this.rdbStore) {
+      return false;
+    }
+
+    try {
+      const updateTime = new Date().toISOString();
+      const sql = 'UPDATE playlistTable SET sortOrder = ?, updateTime = ? WHERE id = ?';
+      await this.rdbStore.executeSql(sql, [sortOrder, updateTime, playlistId]);
+      
+      Logger.info('heanup PlaylistTable', `歌单排序更新成功: ${playlistId}`);
+      return true;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `更新歌单排序失败: ${error.message}`);
+      return false;
+    }
+  }
+
+  /**
+   * 更新歌单歌曲排序
+   */
+  async updatePlaylistSongSortOrder(playlistId: string, songFilePath: string, sortOrder: number): Promise<boolean> {
+    if (!this.rdbStore) {
+      return false;
+    }
+
+    try {
+      const sql = 'UPDATE playlistSongTable SET sortOrder = ? WHERE playlistId = ? AND songFilePath = ?';
+      await this.rdbStore.executeSql(sql, [sortOrder, playlistId, songFilePath]);
+      
+      Logger.info('heanup PlaylistTable', `歌单歌曲排序更新成功: ${songFilePath}`);
+      return true;
+    } catch (error) {
+      Logger.error('heanup PlaylistTable', `更新歌单歌曲排序失败: ${error.message}`);
+      return false;
+    }
+  }
+}

+ 35 - 1
entry/src/main/ets/common/util/RdbUtils.ets

@@ -16,7 +16,7 @@ export default class RdbUtils {
   private tableName: string;
   private tableName: string;
   private sqlCreateTable: string;
   private sqlCreateTable: string;
   private columns: Array<string>;
   private columns: Array<string>;
-  static readonly RDB_TAG: string = 'onecold RdbUtils';
+  static readonly RDB_TAG: string = 'heanup onecold RdbUtils';
 
 
   /**
   /**
    * Rdb数据库配置。
    * Rdb数据库配置。
@@ -92,6 +92,40 @@ export default class RdbUtils {
       'mimeType']
       'mimeType']
   };
   };
 
 
+  /**
+   * 歌单表配置
+   */
+  static readonly PLAYLIST_TABLE: GeneratedObjectLiteralInterface_1 = {
+    tableName: 'playlistTable',
+    sqlCreate: 'CREATE TABLE IF NOT EXISTS playlistTable (\n' +
+      'id TEXT PRIMARY KEY NOT NULL,\n' +
+      'name TEXT NOT NULL,\n' +
+      'coverPath TEXT,\n' +
+      'description TEXT,\n' +
+      'createTime TEXT NOT NULL,\n' +
+      'updateTime TEXT NOT NULL,\n' +
+      'songCount INTEGER DEFAULT 0,\n' +
+      'sortOrder INTEGER DEFAULT 0\n' +
+      ')',
+    columns: ['id', 'name', 'coverPath', 'description', 'createTime', 'updateTime', 'songCount', 'sortOrder']
+  };
+
+  /**
+   * 歌单歌曲关联表配置
+   */
+  static readonly PLAYLIST_SONG_TABLE: GeneratedObjectLiteralInterface_1 = {
+    tableName: 'playlistSongTable',
+    sqlCreate: 'CREATE TABLE IF NOT EXISTS playlistSongTable (\n' +
+      'id TEXT PRIMARY KEY NOT NULL,\n' +
+      'playlistId TEXT NOT NULL,\n' +
+      'songFilePath TEXT NOT NULL,\n' +
+      'addTime TEXT NOT NULL,\n' +
+      'sortOrder INTEGER DEFAULT 0,\n' +
+      'UNIQUE(playlistId, songFilePath)\n' +
+      ')',
+    columns: ['id', 'playlistId', 'songFilePath', 'addTime', 'sortOrder']
+  };
+
   constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
   constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
     this.tableName = tableName;
     this.tableName = tableName;
     this.sqlCreateTable = sqlCreateTable;
     this.sqlCreateTable = sqlCreateTable;

+ 376 - 0
entry/src/main/ets/dialog/AddSongsToPlaylistDialog.ets

@@ -0,0 +1,376 @@
+import { DialogHelper } from '@pura/harmony-dialog';
+import { Playlist } from '../viewmodel/Playlist';
+import { ToastUtil, LogUtil } from '@pura/harmony-utils';
+import { VideoItem } from '../viewmodel/VideoItem';
+import MediaTable from '../common/util/MediaTable';
+import PlaylistTable from '../common/util/PlaylistTable';
+
+/**
+ * 添加歌曲到歌单对话框内容组件
+ */
+@Component
+struct AddSongsToPlaylistDialogContent {
+  @State allSongs: VideoItem[] = []
+  @State selectedSongs: VideoItem[] = []
+  @State isLoading: boolean = true
+  @State searchText: string = ''
+  @State filteredSongs: VideoItem[] = []
+  @State isDbInitialized: boolean = false
+  @Prop playlist: Playlist | null = null
+  private mediaTable: MediaTable = new MediaTable(getContext(this))
+  private playlistTable: PlaylistTable = new PlaylistTable(getContext(this))
+
+  // 回调函数
+  onConfirm?: (songs: VideoItem[]) => void
+  onCancel?: () => void
+
+  aboutToAppear() {
+    LogUtil.info('heanup AddSongsToPlaylistDialogContent aboutToAppear 开始')
+    LogUtil.info('heanup playlist: ' + (this.playlist ? JSON.stringify({ id: this.playlist.id, name: this.playlist.name }) : 'null'))
+    
+    // 初始化数据库连接
+    this.mediaTable.getRdbStore(getContext(this), () => {
+      LogUtil.info('heanup MediaTable 数据库初始化完成')
+      this.isDbInitialized = true
+      this.loadAllSongs()
+    })
+    
+    LogUtil.info('heanup AddSongsToPlaylistDialogContent aboutToAppear 结束')
+  }
+
+  /**
+   * 加载所有歌曲
+   */
+  async loadAllSongs() {
+    if (!this.isDbInitialized) {
+      LogUtil.warn('heanup 数据库未初始化,等待初始化完成')
+      return
+    }
+    
+    try {
+      LogUtil.info('heanup loadAllSongs 开始')
+      this.isLoading = true
+      LogUtil.info('heanup 开始查询所有歌曲')
+      this.allSongs = await this.mediaTable.queryAllVideos()
+      LogUtil.info('heanup 查询到 ' + this.allSongs.length + ' 首歌曲')
+      this.filteredSongs = [...this.allSongs]
+      
+      // 过滤掉已经在歌单中的歌曲
+      if (this.playlist) {
+        LogUtil.info('heanup 开始查询歌单中的歌曲,歌单ID: ' + this.playlist.id)
+        const playlistSongs = await this.playlistTable.queryPlaylistSongs(this.playlist.id)
+        LogUtil.info('heanup 歌单中有 ' + playlistSongs.length + ' 首歌曲')
+        const playlistSongPaths = playlistSongs.map(song => song.songFilePath)
+        this.filteredSongs = this.filteredSongs.filter(song => !playlistSongPaths.includes(song.filePath))
+        this.allSongs = [...this.filteredSongs]
+        LogUtil.info('heanup 过滤后有 ' + this.filteredSongs.length + ' 首歌曲可添加')
+      } else {
+        LogUtil.warn('heanup playlist 为 null')
+      }
+    } catch (error) {
+      LogUtil.error('heanup 加载歌曲列表失败: ' + error)
+      ToastUtil.showToast('加载歌曲列表失败')
+    } finally {
+      LogUtil.info('heanup 设置 isLoading 为 false')
+      this.isLoading = false
+    }
+  }
+
+  /**
+   * 搜索过滤歌曲
+   */
+  filterSongs() {
+    if (!this.searchText.trim()) {
+      this.filteredSongs = [...this.allSongs]
+    } else {
+      const searchLower = this.searchText.toLowerCase()
+      this.filteredSongs = this.allSongs.filter(song => 
+        (song.name && song.name.toLowerCase().includes(searchLower)) ||
+        (song.artist && song.artist.toLowerCase().includes(searchLower)) ||
+        (song.album && song.album.toLowerCase().includes(searchLower))
+      )
+    }
+  }
+
+  build() {
+    Column({ space: 16 }) {
+      // 标题
+      Text('添加歌曲到歌单')
+        .fontSize(18)
+        .fontWeight(FontWeight.Bold)
+        .fontColor($r('app.color.text_color'))
+        .margin({ top: 20 })
+
+      // 搜索框
+      Row({ space: 8 }) {
+        Image($r('app.media.ic_action_search'))
+          .width(20)
+          .height(20)
+          .fillColor($r('app.color.text_color'))
+          .opacity(0.6)
+
+        TextInput({ placeholder: '搜索歌曲、歌手或专辑', text: this.searchText })
+          .layoutWeight(1)
+          .height(40)
+          .backgroundColor($r('app.color.input_background'))
+          .borderRadius(8)
+          .padding({ left: 8, right: 8 })
+          .fontSize(14)
+          .fontColor($r('app.color.text_color'))
+          .onChange((value: string) => {
+            this.searchText = value
+            this.filterSongs()
+          })
+      }
+      .width('100%')
+      .padding(12)
+      .backgroundColor($r('app.color.input_background'))
+      .borderRadius(8)
+
+      // 已选择歌曲数量
+      if (this.selectedSongs.length > 0) {
+        Row() {
+          Text(`已选择 ${this.selectedSongs.length} 首歌曲`)
+            .fontSize(14)
+            .fontColor($r('app.color.theme_color'))
+            .fontWeight(FontWeight.Medium)
+
+          Blank()
+
+          Button('清空')
+            .fontSize(12)
+            .fontColor($r('app.color.text_color'))
+            .backgroundColor(Color.Transparent)
+            .height(30)
+            .padding({ left: 8, right: 8 })
+            .onClick(() => {
+              this.selectedSongs = []
+            })
+        }
+        .width('100%')
+        .padding({ left: 4, right: 4 })
+      }
+
+      // 歌曲列表
+      if (this.isLoading) {
+        Column() {
+          LoadingProgress()
+            .width(40)
+            .height(40)
+            .color($r('app.color.theme_color'))
+          
+          Text('加载中...')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ top: 12 })
+        }
+        .width('100%')
+        .height(300)
+        .justifyContent(FlexAlign.Center)
+      } else if (this.filteredSongs.length === 0) {
+        Column({ space: 12 }) {
+          Image($r('app.media.music_red'))
+            .width(64)
+            .height(64)
+            .opacity(0.3)
+
+          Text(this.searchText ? '没有找到匹配的歌曲' : '没有可添加的歌曲')
+            .fontSize(14)
+            .fontColor('#999999')
+        }
+        .width('100%')
+        .height(300)
+        .justifyContent(FlexAlign.Center)
+      } else {
+        Scroll() {
+          Column({ space: 0 }) {
+            ForEach(this.filteredSongs, (song: VideoItem) => {
+              Row({ space: 12 }) {
+                // 选择框
+                Checkbox({ name: 'song_' + song.id })
+                  .select(this.selectedSongs.some(s => s.id === song.id))
+                  .selectedColor($r('app.color.theme_color'))
+                  .shape(CheckBoxShape.ROUNDED_SQUARE)
+                  .onChange((checked: boolean) => {
+                    if (checked) {
+                      if (!this.selectedSongs.some(s => s.id === song.id)) {
+                        this.selectedSongs.push(song)
+                      }
+                    } else {
+                      const index = this.selectedSongs.findIndex(s => s.id === song.id)
+                      if (index > -1) {
+                        this.selectedSongs.splice(index, 1)
+                      }
+                    }
+                  })
+
+                // 歌曲信息
+                Column({ space: 4 }) {
+                  Text(song.name || '未知歌曲')
+                    .fontSize(14)
+                    .fontColor($r('app.color.text_color'))
+                    .maxLines(1)
+                    .textOverflow({ overflow: TextOverflow.Ellipsis })
+                    .width('100%')
+
+                  Row() {
+                    if (song.artist) {
+                      Text(song.artist)
+                        .fontSize(12)
+                        .fontColor('#999999')
+                        .maxLines(1)
+                        .textOverflow({ overflow: TextOverflow.Ellipsis })
+                        .layoutWeight(1)
+                    }
+
+                    if (song.artist && song.album) {
+                      Text('·')
+                        .fontSize(12)
+                        .fontColor('#999999')
+                    }
+
+                    if (song.album) {
+                      Text(song.album)
+                        .fontSize(12)
+                        .fontColor('#999999')
+                        .maxLines(1)
+                        .textOverflow({ overflow: TextOverflow.Ellipsis })
+                        .layoutWeight(1)
+                    }
+                  }
+                  .width('100%')
+                }
+                .alignItems(HorizontalAlign.Start)
+                .layoutWeight(1)
+              }
+              .width('100%')
+              .padding(12)
+              .borderRadius(8)
+              .onClick(() => {
+                const isSelected = this.selectedSongs.some(s => s.id === song.id)
+                if (isSelected) {
+                  const index = this.selectedSongs.findIndex(s => s.id === song.id)
+                  if (index > -1) {
+                    this.selectedSongs.splice(index, 1)
+                  }
+                } else {
+                  this.selectedSongs.push(song)
+                }
+              })
+            }, (song: VideoItem) => song.id)
+          }
+        }
+        .scrollBar(BarState.Auto)
+        .scrollable(ScrollDirection.Vertical)
+        .height(300)
+      }
+
+      // 按钮区域
+      Row({ space: 12 }) {
+        Button('取消')
+          .width('45%')
+          .height(40)
+          .backgroundColor($r('app.color.cancel_button_background'))
+          .borderRadius(8)
+          .fontSize(14)
+          .fontColor($r('app.color.cancel_button_text'))
+          .onClick(() => {
+            this.onCancel?.()
+            DialogHelper.closeDialog('addSongsToPlaylistDialog')
+          })
+
+        Button(`添加${this.selectedSongs.length > 0 ? `(${this.selectedSongs.length})` : ''}`)
+          .width('45%')
+          .height(40)
+          .backgroundColor($r('app.color.theme_color'))
+          .borderRadius(8)
+          .fontSize(14)
+          .fontColor(Color.White)
+          .enabled(this.selectedSongs.length > 0)
+          .opacity(this.selectedSongs.length > 0 ? 1 : 0.5)
+          .onClick(() => {
+            this.handleConfirm()
+          })
+      }
+      .width('100%')
+      .justifyContent(FlexAlign.SpaceBetween)
+      .margin({ top: 20, bottom: 20 })
+    }
+    .width('90%')
+    .constraintSize({ maxWidth: 400 })
+    .backgroundColor($r('app.color.dialog_background'))
+    .borderRadius(12)
+    .padding({ left: 20, right: 20 })
+  }
+
+  /**
+   * 处理确认操作
+   */
+  private handleConfirm() {
+    if (this.selectedSongs.length === 0) {
+      ToastUtil.showToast('请选择至少一首歌曲')
+      return
+    }
+
+    this.onConfirm?.(this.selectedSongs)
+    DialogHelper.closeDialog('addSongsToPlaylistDialog')
+  }
+}
+
+/**
+ * 添加歌曲到歌单对话框管理器
+ */
+@Component
+export struct AddSongsToPlaylistDialogManager {
+  /**
+   * 添加歌曲到歌单对话框构建器
+   */
+  @Builder
+  buildAddSongsToPlaylistDialog(
+    playlist: Playlist,
+    onConfirm: (songs: VideoItem[]) => void,
+    onCancel?: () => void
+  ) {
+    AddSongsToPlaylistDialogContent({
+      playlist: playlist,
+      onConfirm: onConfirm,
+      onCancel: onCancel
+    })
+  }
+
+  /**
+   * 显示添加歌曲到歌单对话框
+   */
+  showAddSongsToPlaylistDialog(
+    playlist: Playlist,
+    onConfirm: (songs: VideoItem[]) => void,
+    onCancel?: () => void
+  ) {
+    DialogHelper.showCustomContentDialog({
+      dialogId: 'addSongsToPlaylistDialog',
+      title: '添加歌曲到歌单',
+      autoCancel: true,
+      contentBuilder: () => {
+        this.buildAddSongsToPlaylistDialog(playlist, onConfirm, onCancel)
+      },
+      buttons: []
+    })
+  }
+
+  build() {
+  }
+}
+
+// 创建全局实例
+const dialogManager = new AddSongsToPlaylistDialogManager()
+
+/**
+ * 显示添加歌曲到歌单对话框
+ */
+export function showAddSongsToPlaylistDialog(
+  playlist: Playlist,
+  onConfirm: (songs: VideoItem[]) => void,
+  onCancel?: () => void
+) {
+  dialogManager.showAddSongsToPlaylistDialog(playlist, onConfirm, onCancel)
+}

+ 281 - 0
entry/src/main/ets/dialog/AddToPlaylistDialog.ets

@@ -0,0 +1,281 @@
+import { DialogHelper } from '@pura/harmony-dialog';
+import { Playlist } from '../viewmodel/Playlist';
+import { ToastUtil } from '@pura/harmony-utils';
+import { VideoItem } from '../viewmodel/VideoItem';
+
+/**
+ * 添加到歌单对话框内容组件
+ */
+@Component
+struct AddToPlaylistDialogContent {
+  @State playlists: Playlist[] = []
+  @State selectedPlaylistId: string = ''
+  private currentSong?: VideoItem
+
+  // 回调函数
+  onConfirm?: (playlistId: string) => void
+  onCancel?: () => void
+  onCreateNew?: () => void
+
+  build() {
+    Column({ space: 16 }) {
+      // 标题
+      Text('添加到歌单')
+        .fontSize(18)
+        .fontWeight(FontWeight.Bold)
+        .fontColor($r('app.color.text_color'))
+        .margin({ top: 20 })
+
+      // 歌曲信息
+      if (this.currentSong) {
+        Row({ space: 12 }) {
+          // 封面
+          Image(this.currentSong.pixelMap || $r('app.media.icon'))
+            .width(48)
+            .height(48)
+            .borderRadius(8)
+            .objectFit(ImageFit.Cover)
+
+          // 歌曲名和艺术家
+          Column({ space: 4 }) {
+            Text(this.currentSong.name || '未知歌曲')
+              .fontSize(14)
+              .fontColor($r('app.color.text_color'))
+              .maxLines(1)
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+
+            Text(this.currentSong.artist || '未知艺术家')
+              .fontSize(12)
+              .fontColor('#999999')
+              .maxLines(1)
+              .textOverflow({ overflow: TextOverflow.Ellipsis })
+          }
+          .alignItems(HorizontalAlign.Start)
+          .layoutWeight(1)
+        }
+        .width('100%')
+        .padding(12)
+        .backgroundColor($r('app.color.input_background'))
+        .borderRadius(8)
+      }
+
+      // 创建新歌单按钮
+      Button() {
+        Row({ space: 8 }) {
+          Image($r('sys.symbol.plus'))
+            .width(20)
+            .height(20)
+            .fillColor($r('app.color.theme_color'))
+
+          Text('创建新歌单')
+            .fontSize(14)
+            .fontColor($r('app.color.theme_color'))
+        }
+      }
+      .width('100%')
+      .height(44)
+      .backgroundColor($r('app.color.input_background'))
+      .borderRadius(8)
+      .onClick(() => {
+        this.onCreateNew?.()
+        DialogHelper.closeDialog('addToPlaylistDialog')
+      })
+
+      // 分隔线
+      if (this.playlists.length > 0) {
+        Divider()
+          .color($r('app.color.divider_color'))
+      }
+
+      // 歌单列表
+      if (this.playlists.length > 0) {
+        Text('选择歌单')
+          .fontSize(14)
+          .fontColor('#999999')
+          .alignSelf(ItemAlign.Start)
+
+        Scroll() {
+          Column({ space: 8 }) {
+            ForEach(this.playlists, (playlist: Playlist) => {
+              Row({ space: 12 }) {
+                // 封面
+                Image(playlist.coverPath || $r('app.media.icon'))
+                  .width(48)
+                  .height(48)
+                  .borderRadius(8)
+                  .objectFit(ImageFit.Cover)
+
+                // 歌单信息
+                Column({ space: 4 }) {
+                  Text(playlist.name)
+                    .fontSize(14)
+                    .fontColor($r('app.color.text_color'))
+                    .maxLines(1)
+                    .textOverflow({ overflow: TextOverflow.Ellipsis })
+
+                  Text(`${playlist.songCount} 首歌曲`)
+                    .fontSize(12)
+                    .fontColor('#999999')
+                }
+                .alignItems(HorizontalAlign.Start)
+                .layoutWeight(1)
+
+                // 选中标记
+                if (this.selectedPlaylistId === playlist.id) {
+                  Image($r('sys.symbol.checkmark'))
+                    .width(20)
+                    .height(20)
+                    .fillColor($r('app.color.theme_color'))
+                }
+              }
+              .width('100%')
+              .padding(12)
+              .backgroundColor(this.selectedPlaylistId === playlist.id ?
+                '#E6F0FF' : $r('app.color.input_background'))
+              .borderRadius(8)
+              .onClick(() => {
+                this.selectedPlaylistId = playlist.id
+              })
+            }, (playlist: Playlist) => playlist.id)
+          }
+        }
+        .scrollBar(BarState.Auto)
+        .scrollable(ScrollDirection.Vertical)
+        .height(300)
+      } else {
+        Column({ space: 12 }) {
+          Image($r('app.media.icon'))
+            .width(64)
+            .height(64)
+            .opacity(0.3)
+
+          Text('暂无歌单')
+            .fontSize(14)
+            .fontColor('#999999')
+
+          Text('点击上方按钮创建第一个歌单吧')
+            .fontSize(12)
+            .fontColor('#999999')
+        }
+        .width('100%')
+        .padding(20)
+        .justifyContent(FlexAlign.Center)
+      }
+
+      // 按钮区域
+      Row({ space: 12 }) {
+        Button('取消')
+          .width('45%')
+          .height(40)
+          .backgroundColor($r('app.color.cancel_button_background'))
+          .borderRadius(8)
+          .fontSize(14)
+          .fontColor($r('app.color.cancel_button_text'))
+          .onClick(() => {
+            this.onCancel?.()
+            DialogHelper.closeDialog('addToPlaylistDialog')
+          })
+
+        Button('添加')
+          .width('45%')
+          .height(40)
+          .backgroundColor($r('app.color.theme_color'))
+          .borderRadius(8)
+          .fontSize(14)
+          .fontColor(Color.White)
+          .enabled(this.selectedPlaylistId !== '')
+          .opacity(this.selectedPlaylistId !== '' ? 1 : 0.5)
+          .onClick(() => {
+            this.handleConfirm()
+          })
+      }
+      .width('100%')
+      .justifyContent(FlexAlign.SpaceBetween)
+      .margin({ top: 20, bottom: 20 })
+    }
+    .width('90%')
+    .constraintSize({ maxWidth: 400 })
+    .backgroundColor($r('app.color.dialog_background'))
+    .borderRadius(12)
+    .padding({ left: 20, right: 20 })
+  }
+
+  /**
+   * 处理确认操作
+   */
+  private handleConfirm() {
+    if (!this.selectedPlaylistId) {
+      ToastUtil.showToast('请选择一个歌单')
+      return
+    }
+
+    this.onConfirm?.(this.selectedPlaylistId)
+    DialogHelper.closeDialog('addToPlaylistDialog')
+  }
+}
+
+/**
+ * 添加到歌单对话框管理器
+ */
+@Component
+export struct AddToPlaylistDialogManager {
+  /**
+   * 添加到歌单对话框构建器
+   */
+  @Builder
+  buildAddToPlaylistDialog(
+    song: VideoItem,
+    playlists: Playlist[],
+    onConfirm: (playlistId: string) => void,
+    onCancel?: () => void,
+    onCreateNew?: () => void
+  ) {
+    AddToPlaylistDialogContent({
+      currentSong: song,
+      playlists: playlists,
+      onConfirm: onConfirm,
+      onCancel: onCancel,
+      onCreateNew: onCreateNew
+    })
+  }
+
+  /**
+   * 显示添加到歌单对话框
+   */
+  showAddToPlaylistDialog(
+    song: VideoItem,
+    playlists: Playlist[],
+    onConfirm: (playlistId: string) => void,
+    onCancel?: () => void,
+    onCreateNew?: () => void
+  ) {
+    DialogHelper.showCustomContentDialog({
+      dialogId: 'addToPlaylistDialog',
+      title: '添加到歌单',
+      autoCancel: true,
+      contentBuilder: () => {
+        this.buildAddToPlaylistDialog(song, playlists, onConfirm, onCancel, onCreateNew)
+      },
+      buttons: []
+    })
+  }
+
+  build() {
+  }
+}
+
+// 创建全局实例
+const dialogManager = new AddToPlaylistDialogManager()
+
+/**
+ * 显示添加到歌单对话框
+ */
+export function showAddToPlaylistDialog(
+  song: VideoItem,
+  playlists: Playlist[],
+  onConfirm: (playlistId: string) => void,
+  onCancel?: () => void,
+  onCreateNew?: () => void
+) {
+  dialogManager.showAddToPlaylistDialog(song, playlists, onConfirm, onCancel, onCreateNew)
+}

+ 232 - 0
entry/src/main/ets/dialog/PlaylistDialog.ets

@@ -0,0 +1,232 @@
+import { DialogHelper } from '@pura/harmony-dialog';
+import { Playlist } from '../viewmodel/Playlist';
+import { ToastUtil } from '@pura/harmony-utils';
+
+/**
+ * 歌单对话框内容组件
+ */
+@Component
+struct PlaylistDialogContent {
+  @State playlistName: string = ''
+  @State playlistDescription: string = ''
+  @State isEditMode: boolean = false
+  @State originalPlaylist: Playlist | null = null
+  
+  // 回调函数
+  onConfirm?: (name: string, description: string) => void
+  onCancel?: () => void
+
+  aboutToAppear() {
+    if (this.originalPlaylist) {
+      this.isEditMode = true
+      this.playlistName = this.originalPlaylist.name
+      this.playlistDescription = this.originalPlaylist.description || ''
+    }
+  }
+
+  build() {
+    Column({ space: 20 }) {
+      // 标题
+      Text(this.isEditMode ? '编辑歌单' : '创建歌单')
+        .fontSize(18)
+        .fontWeight(FontWeight.Bold)
+        .fontColor($r('app.color.text_color'))
+        .margin({ top: 20 })
+
+      // 歌单名称输入框
+      Column({ space: 8 }) {
+        Text('歌单名称')
+          .fontSize(14)
+          .fontColor($r('app.color.text_color'))
+          .alignSelf(ItemAlign.Start)
+        
+        TextInput({ placeholder: '请输入歌单名称', text: this.playlistName })
+          .width('100%')
+          .height(40)
+          .backgroundColor($r('app.color.input_background'))
+          .borderRadius(8)
+          .padding({ left: 12, right: 12 })
+          .fontSize(14)
+          .fontColor($r('app.color.text_color'))
+          .onChange((value: string) => {
+            this.playlistName = value
+          })
+      }
+      .width('100%')
+      .alignItems(HorizontalAlign.Start)
+
+      // 歌单描述输入框
+      Column({ space: 8 }) {
+        Text('歌单描述(可选)')
+          .fontSize(14)
+          .fontColor($r('app.color.text_color'))
+          .alignSelf(ItemAlign.Start)
+        
+        TextArea({ placeholder: '请输入歌单描述', text: this.playlistDescription })
+          .width('100%')
+          .height(80)
+          .backgroundColor($r('app.color.input_background'))
+          .borderRadius(8)
+          .padding({ left: 12, right: 12, top: 8, bottom: 8 })
+          .fontSize(14)
+          .fontColor($r('app.color.text_color'))
+          .onChange((value: string) => {
+            this.playlistDescription = value
+          })
+      }
+      .width('100%')
+      .alignItems(HorizontalAlign.Start)
+
+      // 按钮区域
+      Row({ space: 12 }) {
+        Button('取消')
+          .width('45%')
+          .height(40)
+          .backgroundColor($r('app.color.cancel_button_background'))
+          .borderRadius(8)
+          .fontSize(14)
+          .fontColor($r('app.color.cancel_button_text'))
+          .onClick(() => {
+            this.onCancel?.()
+            // 关闭对话框
+            DialogHelper.closeDialog(this.isEditMode ? 'editPlaylistDialog' : 'createPlaylistDialog')
+          })
+
+        Button(this.isEditMode ? '保存' : '创建')
+          .width('45%')
+          .height(40)
+          .backgroundColor($r('app.color.theme_color'))
+          .borderRadius(8)
+          .fontSize(14)
+          .fontColor(Color.White)
+          .onClick(() => {
+            this.handleConfirm()
+          })
+      }
+      .width('100%')
+      .justifyContent(FlexAlign.SpaceBetween)
+      .margin({ top: 20, bottom: 20 })
+    }
+    .width('90%')
+    .backgroundColor($r('app.color.dialog_background'))
+    .borderRadius(12)
+    .padding({ left: 20, right: 20 })
+  }
+
+  /**
+   * 处理确认操作
+   */
+  private handleConfirm() {
+    if (!this.playlistName.trim()) {
+      ToastUtil.showToast('请输入歌单名称')
+      return
+    }
+
+    if (this.playlistName.trim().length > 50) {
+      ToastUtil.showToast('歌单名称不能超过50个字符')
+      return
+    }
+
+    if (this.playlistDescription.trim().length > 200) {
+      ToastUtil.showToast('歌单描述不能超过200个字符')
+      return
+    }
+
+    this.onConfirm?.(this.playlistName.trim(), this.playlistDescription.trim())
+    // 关闭对话框
+    DialogHelper.closeDialog(this.isEditMode ? 'editPlaylistDialog' : 'createPlaylistDialog')
+  }
+}
+
+/**
+ * 歌单对话框管理器
+ */
+@Component
+export struct PlaylistDialogManager {
+  /**
+   * 创建歌单对话框构建器
+   */
+  @Builder
+  buildCreatePlaylistDialog(onConfirm: (name: string, description: string) => void, onCancel?: () => void) {
+    PlaylistDialogContent({
+      onConfirm: onConfirm,
+      onCancel: onCancel
+    })
+  }
+
+  /**
+   * 编辑歌单对话框构建器
+   */
+  @Builder
+  buildEditPlaylistDialog(playlist: Playlist, onConfirm: (name: string, description: string) => void, onCancel?: () => void) {
+    PlaylistDialogContent({
+      originalPlaylist: playlist,
+      onConfirm: onConfirm,
+      onCancel: onCancel
+    })
+  }
+
+  /**
+   * 显示创建歌单对话框
+   */
+  showCreatePlaylistDialog(
+    onConfirm: (name: string, description: string) => void,
+    onCancel?: () => void
+  ) {
+    DialogHelper.showCustomContentDialog({
+      dialogId: 'createPlaylistDialog',
+      title: '创建歌单',
+      autoCancel: true,
+      contentBuilder: () => {
+        this.buildCreatePlaylistDialog(onConfirm, onCancel)
+      },
+      buttons: []
+    })
+  }
+
+  /**
+   * 显示编辑歌单对话框
+   */
+  showEditPlaylistDialog(
+    playlist: Playlist,
+    onConfirm: (name: string, description: string) => void,
+    onCancel?: () => void
+  ) {
+    DialogHelper.showCustomContentDialog({
+      dialogId: 'editPlaylistDialog',
+      title: '编辑歌单',
+      autoCancel: true,
+      contentBuilder: () => {
+        this.buildEditPlaylistDialog(playlist, onConfirm, onCancel)
+      },
+      buttons: []
+    })
+  }
+
+  build() {
+  }
+}
+
+// 创建全局实例
+const dialogManager = new PlaylistDialogManager()
+
+/**
+ * 显示创建歌单对话框
+ */
+export function showCreatePlaylistDialog(
+  onConfirm: (name: string, description: string) => void,
+  onCancel?: () => void
+) {
+  dialogManager.showCreatePlaylistDialog(onConfirm, onCancel)
+}
+
+/**
+ * 显示编辑歌单对话框
+ */
+export function showEditPlaylistDialog(
+  playlist: Playlist,
+  onConfirm: (name: string, description: string) => void,
+  onCancel?: () => void
+) {
+  dialogManager.showEditPlaylistDialog(playlist, onConfirm, onCancel)
+}

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

+ 301 - 33
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 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 mainViewModel, { MainViewModel } from '../viewmodel/MainViewModel';
 import ItemData from '../viewmodel/ItemData';
 import ItemData from '../viewmodel/ItemData';
 import type { sysResource } from '../viewmodel/ItemData';
 import type { sysResource } from '../viewmodel/ItemData';
 import { CommonConstants } from '../common/constants/CommonConstants';
 import { CommonConstants } from '../common/constants/CommonConstants';
+import { EventConstants } from '../common/constants/EventConstants';
 import { VideoItem } from '../viewmodel/VideoItem';
 import { VideoItem } from '../viewmodel/VideoItem';
 import ScreenUtil from '../common/util/ScreenUtil';
 import ScreenUtil from '../common/util/ScreenUtil';
 import { Utility } from '../common/util/Utility';
 import { Utility } from '../common/util/Utility';
@@ -27,7 +28,7 @@ import { resourceManager } from '@kit.LocalizationKit';
 import { systemShare } from '@kit.ShareKit';
 import { systemShare } from '@kit.ShareKit';
 import { uniformTypeDescriptor } from '@kit.ArkData';
 import { uniformTypeDescriptor } from '@kit.ArkData';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { hilog } from '@kit.PerformanceAnalysisKit';
-import { DialogHelper } from '@pura/harmony-dialog';
+import { DialogHelper, DialogAction } from '@pura/harmony-dialog';
 import UserUtil from '../common/util/UserUtil';
 import UserUtil from '../common/util/UserUtil';
 import json from '@ohos.util.json';
 import json from '@ohos.util.json';
 import { UserCenter } from './UserCenter';
 import { UserCenter } from './UserCenter';
@@ -40,6 +41,9 @@ import OnlineUpdateLog from '../dialog/OnlineUpdateLog';
 import { LocalMusic } from '../view/LocalMusic';
 import { LocalMusic } from '../view/LocalMusic';
 import { ChartsCount } from './ChartsCount';
 import { ChartsCount } from './ChartsCount';
 import { image } from '@kit.ImageKit';
 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';
 // import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 
 
@@ -53,6 +57,11 @@ const TAG = 'NewIndex'; // 日志标签
 @Entry
 @Entry
 @Component
 @Component
 struct NewIndex {
 struct NewIndex {
+  /** 页面上下文 */
+  context = this.getUIContext().getHostContext() as common.UIAbilityContext
+  @Provide currentSongList: Array<VideoItem> = []//当前歌单
+  @Provide currentSongListName:string = '' //当前歌单名称
+  @Provide  currentSongListID:string='' //当前歌单ID
   @State isDarkMode: boolean = false
   @State isDarkMode: boolean = false
   /** 列表滚动器,用于抽屉菜单列表滚动 */
   /** 列表滚动器,用于抽屉菜单列表滚动 */
   private scroller: Scroller = new Scroller();
   private scroller: Scroller = new Scroller();
@@ -75,8 +84,6 @@ struct NewIndex {
   @Provide('isZero') isZero: boolean = false;
   @Provide('isZero') isZero: boolean = false;
   /** 是否显示赞助入口 */
   /** 是否显示赞助入口 */
   @State isShowSponsorship: boolean = false
   @State isShowSponsorship: boolean = false
-  /** 页面上下文 */
-  context = this.getUIContext().getHostContext() as common.UIAbilityContext
   /** 音乐是否为零状态(预留) */
   /** 音乐是否为零状态(预留) */
   @Provide('isMusicZero') isMusicZero: boolean = false;
   @Provide('isMusicZero') isMusicZero: boolean = false;
   @State isShowTitleBar: boolean = true //是否显示分类导航条
   @State isShowTitleBar: boolean = true //是否显示分类导航条
@@ -152,6 +159,12 @@ struct NewIndex {
     }
     }
   })
   })
   @State tabSelectedIndexes: number[] = [0]
   @State tabSelectedIndexes: number[] = [0]
+  
+  // 歌单相关状态变量
+  @State playlistList: Playlist[] = []
+  @State isShowCreatePlaylistDialog: boolean = false
+  @State selectedPlaylist: Playlist | null = null
+  private playlistTable: PlaylistTable | null = null
 
 
   /**
   /**
    * 返回键处理逻辑:
    * 返回键处理逻辑:
@@ -163,7 +176,7 @@ struct NewIndex {
       (this.modeType !== 0 && this.isCanBack)) {
       (this.modeType !== 0 && this.isCanBack)) {
       console.info('onecold 返回键处理逻辑:发送音频广播通知更新列表');
       console.info('onecold 返回键处理逻辑:发送音频广播通知更新列表');
       const eventData: emitter.EventData = {};
       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){
     }else if(this.mType > 0){
       this.getUIContext()?.animateTo({ duration: 555 }, () => {
       this.getUIContext()?.animateTo({ duration: 555 }, () => {
         // 动画闭包内控制Image组件的出现和消失
         // 动画闭包内控制Image组件的出现和消失
@@ -201,8 +214,8 @@ struct NewIndex {
 
 
 
 
   onPageShow() {
   onPageShow() {
-
-
+    // 加载歌单列表
+    this.loadPlaylistList()
   }
   }
   /**
   /**
    * 页面显示生命周期钩子
    * 页面显示生命周期钩子
@@ -244,17 +257,15 @@ struct NewIndex {
     // if (this.isLogin) {
     // if (this.isLogin) {
     //   void this.fetchUserInfo();
     //   void this.fetchUserInfo();
     // }
     // }
-    console.log('Heanup isLogin:' + this.isLogin)
-    console.log('Heanup Utility.isNobleForOld():' + Utility.isNobleForOld())
 
 
     await UserUtil.fetchUserInfo();
     await UserUtil.fetchUserInfo();
     this.refreshUserInfoState();
     this.refreshUserInfoState();
-    let changeUserState: emitter.InnerEvent = { eventId: 1001 }
+    let changeUserState: emitter.InnerEvent = { eventId: EventConstants.EVENT_USER_STATE_CHANGE }
     emitter.on(changeUserState, () => {
     emitter.on(changeUserState, () => {
       this.refreshUserInfoState();
       this.refreshUserInfoState();
     });
     });
 
 
-    let eventSetting: emitter.InnerEvent = { eventId: 333 }
+    let eventSetting: emitter.InnerEvent = { eventId: EventConstants.EVENT_SETTING_UPDATE }
     // 监听广播事件(通用设置配置更新)
     // 监听广播事件(通用设置配置更新)
     emitter.on(eventSetting, (eventData: emitter.EventData) => {
     emitter.on(eventSetting, (eventData: emitter.EventData) => {
       this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
       this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
@@ -272,6 +283,14 @@ struct NewIndex {
 
 
     // 检查并显示更新日志
     // 检查并显示更新日志
     this.checkAndShowUpdateLog();
     this.checkAndShowUpdateLog();
+
+    // 初始化歌单数据库
+    await this.initPlaylistTable();
+
+    // 监听歌单刷新事件
+    emitter.on({ eventId: EventConstants.EVENT_PLAYLIST_REFRESH }, (eventData: emitter.EventData) => {
+      this.loadPlaylistList()
+    });
   }
   }
 
 
   /**
   /**
@@ -300,8 +319,11 @@ struct NewIndex {
   aboutToDisappear() {
   aboutToDisappear() {
     console.info('NewIndex aboutToDisappear');
     console.info('NewIndex aboutToDisappear');
     this.breakpointSystem.unregister();
     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(EventConstants.EVENT_PLAYLIST_REFRESH);
 
 
   }
   }
 
 
@@ -387,23 +409,6 @@ struct NewIndex {
   isHiCar() {
   isHiCar() {
 
 
     return this.isHiCarStatus&&this.curDisplayIsHiCar;
     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;
     return false;
   }
   }
@@ -633,8 +638,8 @@ struct NewIndex {
       ListItemGroup({ header: this.buildUserInfoCard() }) {
       ListItemGroup({ header: this.buildUserInfoCard() }) {
         if(this.tabSelectedIndexes[0]==0){//分类
         if(this.tabSelectedIndexes[0]==0){//分类
           this.buildTabCate()
           this.buildTabCate()
-        }else{//歌单,这里写一个歌单的listItem
-
+        }else{//歌单
+          this.buildPlaylistTab()
         }
         }
 
 
       }
       }
@@ -992,6 +997,269 @@ struct NewIndex {
   isLoginChange() {
   isLoginChange() {
     this.refreshUserInfoState();
     this.refreshUserInfoState();
   }
   }
+
+  /**
+   * 构建歌单tab内容
+   */
+  @Builder
+  buildPlaylistTab() {
+    // 创建歌单按钮
+    ListItem() {
+      Button({ type: ButtonType.Capsule, stateEffect: true }) {
+        Row() {
+          SymbolGlyph($r('sys.symbol.plus_circle'))
+            .fontSize(22)
+            .fontColor([this.themeColor])
+            .effectStrategy(SymbolEffectStrategy.HIERARCHICAL)
+            .alignSelf(ItemAlign.Center)
+            .margin({ left: 25 })
+          
+          Text('创建歌单')
+            .margin({ left: 10, right: 20 })
+            .fontSize(15)
+            .fontColor($r('app.color.index_tab_font_color'))
+            .fontWeight(480)
+          
+          Blank()
+          
+          Image($r('app.media.arrow_right'))
+            .width(22)
+            .height(22)
+            .margin({ left: 20, right: 0 })
+            .align(Alignment.Center)
+        }
+        .width('100%')
+        .height(55)
+      }
+      .backgroundColor(Color.Transparent)
+      .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
+      .onClick(() => {
+        this.showCreatePlaylistDialog()
+      })
+    }
+    
+    // 歌单列表
+    ForEach(this.playlistList, (playlist: Playlist) => {
+      ListItem() {
+        Button({ type: ButtonType.Capsule, stateEffect: true }) {
+          Row() {
+            Image(playlist.coverPath || $r('app.media.hm_playlist'))
+              .width(22)
+              .height(22)
+              .margin({ left: 25 })
+              .borderRadius(4)
+              .fillColor(this.themeColor)
+              .clip(true)
+            
+            Column() {
+              Text(playlist.name)
+                .margin({ left: 10, right: 20 })
+                .fontSize(15)
+                .fontColor($r('app.color.index_tab_font_color'))
+                .fontWeight(480)
+                .maxLines(1)
+                .textOverflow({ overflow: TextOverflow.Ellipsis })
+              
+              Text(`${playlist.songCount}首`)
+                .margin({ left: 10, right: 20 })
+                .fontSize(12)
+                .fontColor($r('app.color.index_tab_font_color'))
+                .opacity(0.7)
+            }
+            .alignItems(HorizontalAlign.Start)
+            
+            Blank()
+            
+            Image($r('app.media.arrow_right'))
+              .width(22)
+              .height(22)
+              .margin({ left: 20, right: 0 })
+              .align(Alignment.Center)
+          }
+          .width('100%')
+          .height(55)
+        }
+        .backgroundColor(Color.Transparent)
+        .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
+        .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()
+        })
+        .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('歌单删除失败')
+              }
+            }
+
+          }
+        }
+      })
+    }
+  }
+
+  /**
+   * 显示创建歌单对话框
+   */
+  showCreatePlaylistDialog() {
+    if (!this.playlistTable) {
+      ToastUtil.showToast('歌单功能初始化中,请稍后再试')
+      return
+    }
+
+    DialogHelper.showTextInputDialog({
+      title: '创建歌单',
+      maskColor: Color.Transparent,
+      text: '',
+      placeholder: '请输入歌单名称',
+      onAction: async (action, dialogId, content) => {
+        if (action === DialogAction.TWO && content.trim()) {
+          // 创建歌单
+          const success = await this.playlistTable!.createPlaylist(content.trim())
+          if (success) {
+            ToastUtil.showToast('歌单创建成功')
+            // 刷新歌单列表
+            await this.loadPlaylistList()
+            // 发送歌单刷新事件
+            emitter.emit({ eventId: EventConstants.EVENT_PLAYLIST_REFRESH }, {})
+          } else {
+            ToastUtil.showToast('歌单创建失败')
+          }
+        }
+      }
+    })
+  }
+
+  /**
+   * 打开歌单详情
+   */
+  openPlaylist(playlist: Playlist) {
+    try {
+      // 跳转到歌单详情页面
+      router.pushUrl({
+        url: 'pages/PlaylistDetailPage',
+        params: {
+          playlist: playlist
+        }
+      }).catch((err: Error) => {
+        console.error('跳转到歌单详情页面失败:', err.message);
+        ToastUtil.showToast('打开歌单失败');
+      });
+    } catch (error) {
+      console.error('打开歌单失败:', error);
+      ToastUtil.showToast('打开歌单失败');
+    }
+  }
+
+
+  /**
+   * 初始化歌单数据库
+   */
+  async initPlaylistTable() {
+    try {
+      this.playlistTable = new PlaylistTable(this.context)
+      console.info('歌单数据库初始化成功')
+
+      // 等待数据库初始化完成后再加载数据
+      setTimeout(async () => {
+        await this.loadPlaylistList()
+      }, 500) // 延迟500ms确保数据库初始化完成
+    } catch (error) {
+      console.error('初始化歌单数据库失败:', error)
+    }
+  }
+
+  /**
+   * 加载歌单列表
+   */
+  async loadPlaylistList() {
+    try {
+      if (this.playlistTable) {
+        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('歌单表未初始化')
+      }
+    } catch (error) {
+      console.error('加载歌单列表失败:', error)
+    }
+  }
 }
 }
 
 
 // 1. 工具函数:将 #RRGGBB 字符串和透明度转为 'rgba(r,g,b,a)' 字符串,深色模式下可返回深色变体
 // 1. 工具函数:将 #RRGGBB 字符串和透明度转为 'rgba(r,g,b,a)' 字符串,深色模式下可返回深色变体

+ 1260 - 0
entry/src/main/ets/pages/PlaylistDetailPage.ets

@@ -0,0 +1,1260 @@
+import { Playlist, PlaylistSong } from '../viewmodel/Playlist';
+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, 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 PlaylistEventData {
+  playlistId: string;
+  playlistName: string;
+  songCount: number;
+  startIndex: number;
+  songFilePaths: string[];
+}
+
+/**
+ * 歌单详情页面
+ * 展示歌单信息和歌曲列表
+ */
+@Entry
+@Component
+export struct PlaylistDetailPage {
+  context = this.getUIContext().getHostContext() as common.UIAbilityContext
+  @State playlist: Playlist | null = null
+  @State songList: VideoItem[] = []
+  @State isLoading: boolean = true
+  @State isShowEditDialog: boolean = false
+  @State isPlaying: boolean = false
+  @State curIndex: number = -1
+  @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']) {
+      this.playlist = params['playlist'] as Playlist
+      this.playlistId = this.playlist.id
+      this.loadPlaylistDetail()
+    }
+
+    // 监听播放状态变化
+    this.setupPlaybackStatusListener()
+  }
+
+  aboutToDisappear() {
+    // 移除事件监听
+    this.removePlaybackStatusListener()
+  }
+
+  /**
+   * 加载歌单详情
+   */
+  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 convertPlaylistSongsToVideoItems(this.context,playlistSongs)
+
+      // 歌单加载完成后,加载当前播放状态
+      this.loadCurrentPlaybackStatus()
+    } catch (error) {
+      LogUtil.error('heanup 加载歌单详情失败: ' + error)
+      ToastUtil.showToast('加载歌单详情失败')
+    } finally {
+      this.isLoading = false
+      // 启动页面进入动画
+      this.animatePageEntry()
+    }
+  }
+
+  /**
+   * 格式化时长显示
+   */
+  formatDuration(duration?: number): string {
+    if (!duration || duration <= 0) {
+      return ''
+    }
+
+    const minutes = Math.floor(duration / 60)
+    const seconds = Math.floor(duration % 60)
+    return `${minutes}:${seconds.toString().padStart(2, '0')}`
+  }
+
+  /**
+   * 计算歌单总时长
+   */
+  calculateTotalDuration(): number {
+    let total = 0
+    for (const song of this.songList) {
+      const duration = song.duration || 0
+      total = total + (typeof duration === 'number' ? duration : 0)
+    }
+    return total
+  }
+
+  /**
+   * 格式化总时长显示
+   */
+  formatTotalDuration(totalSeconds: number): string {
+    if (totalSeconds <= 0) {
+      return ''
+    }
+
+    const hours = Math.floor(totalSeconds / 3600)
+    const minutes = Math.floor((totalSeconds % 3600) / 60)
+
+    if (hours > 0) {
+      return `${hours}小时${minutes}分钟`
+    } else {
+      return `${minutes}分钟`
+    }
+  }
+
+  /**
+   * 页面入场动画
+   */
+  animatePageEntry() {
+    animateTo({
+      duration: 600,
+      curve: Curve.EaseOut,
+      delay: 100,
+      onFinish: () => {
+        this.showContent = true
+      }
+    }, () => {
+      this.pageOpacity = 1
+      this.contentScale = 1
+    })
+  }
+
+  /**
+   * 设置播放状态监听
+   */
+  setupPlaybackStatusListener() {
+    try {
+      // 监听播放状态变化事件
+      const eventPlaybackStatus: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYBACK_STATUS }
+      emitter.on(eventPlaybackStatus, (eventData: emitter.EventData) => {
+        LogUtil.info('heanup PlaylistDetailPage 收到播放状态变化事件:'+ JSON.stringify(eventData))
+        if (eventData.data) {
+          const data = eventData.data as Record<string, Object>
+          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=${oldIsPlaying}->${this.isPlaying}, curIndex=${oldCurIndex}->${this.curIndex}`)
+        }
+      })
+    } catch (error) {
+      LogUtil.error('heanup 设置播放状态监听失败: ' + error)
+    }
+  }
+
+  /**
+   * 移除播放状态监听
+   */
+  removePlaybackStatusListener() {
+    try {
+      emitter.off(EventConstants.EVENT_PLAYBACK_STATUS)
+    } catch (error) {
+      LogUtil.error('heanup 移除播放状态监听失败: ' + error)
+    }
+  }
+
+  /**
+   * 加载当前播放状态
+   */
+  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.info(`heanup 当前播放的歌曲不在本歌单中`)
+        }
+      } else {
+        LogUtil.info(`heanup 当前没有播放歌曲`)
+      }
+    } catch (error) {
+      LogUtil.error('heanup 加载当前播放状态失败: ' + error)
+    }
+  }
+
+
+
+  /**
+   * 播放歌单
+   */
+  playPlaylist() {
+    if (this.songList.length === 0) {
+      ToastUtil.showToast('歌单为空')
+      return
+    }
+
+    // 发送播放歌单事件
+    const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY }
+
+    LogUtil.info(`heanup 准备发送播放事件,playlist: ${this.playlist ? '存在' : '不存在'}, songs: ${this.songList.length}首`)
+
+    // 构建歌单播放事件数据
+    const playlistData: PlaylistEventData = {
+      playlistId: this.playlist?.id || '',
+      playlistName: this.playlist?.name || '',
+      songCount: this.songList.length,
+      startIndex: 0,
+      // 只发送歌曲的必要信息
+      songFilePaths: this.songList.map(song => song.filePath)
+    };
+
+    LogUtil.info(`heanup 发送歌单播放事件数据: ${JSON.stringify(playlistData)}`)
+
+    const eventData: emitter.EventData = {
+      data: playlistData
+    };
+
+    emitter.emit(eventPlaylistPlay, eventData)
+    
+    ToastUtil.showToast('开始播放歌单')
+  }
+
+  /**
+   * 撌放指定歌曲
+   */
+  playSong(song: VideoItem, index: number) {
+    LogUtil.info(`heanup === playSong 方法被调用 ===`)
+    LogUtil.info(`heanup 播放指定歌曲: ${song.name}, 索引: ${index}`)
+    LogUtil.info(`heanup 歌曲列表长度: ${this.songList.length}`)
+    LogUtil.info(`heanup 歌单ID: ${this.playlist?.id}, 歌单名称: ${this.playlist?.name}`)
+
+    // 检查歌曲文件路径
+    LogUtil.info(`heanup 所有歌曲文件路径: ${JSON.stringify(this.songList.map(s => s.filePath))}`)
+
+    // 发送播放歌单事件
+    const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY }
+    const playlistData: PlaylistEventData = {
+      playlistId: this.playlist?.id || '',
+      playlistName: this.playlist?.name || '',
+      songCount: this.songList.length,
+      startIndex: index,
+      // 只发送所有歌曲的文件路径
+      songFilePaths: this.songList.map(s => s.filePath)
+    };
+
+    LogUtil.info(`heanup 准备发送事件,eventId: ${eventPlaylistPlay.eventId}`)
+    LogUtil.info(`heanup 发送歌单播放事件数据: ${JSON.stringify(playlistData)}`)
+
+    const eventData: emitter.EventData = {
+      data: playlistData
+    };
+
+    LogUtil.info(`heanup eventData对象: ${JSON.stringify(eventData)}`)
+    emitter.emit(eventPlaylistPlay, eventData)
+  }
+
+  /**
+   * 编辑歌单
+   */
+  editPlaylist() {
+    if (this.playlist) {
+      showEditPlaylistDialog(
+        this.playlist,
+        async (name: string, description: string) => {
+          if (this.playlist) {
+            this.playlist.name = name
+            this.playlist.description = description
+            const success = await this.playlistTable.updatePlaylist(this.playlist.id, name, description)
+            if (success) {
+              ToastUtil.showToast('歌单更新成功')
+              // 发送刷新事件
+              const eventRefresh: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_REFRESH }
+              emitter.emit(eventRefresh, {})
+            } else {
+              ToastUtil.showToast('歌单更新失败')
+            }
+          }
+        }
+      )
+    }
+  }
+
+  /**
+   * 删除歌单
+   */
+  async deletePlaylist() {
+    if (this.playlist) {
+      // 显示确认对话框
+      AlertDialog.show({
+        title: '删除歌单',
+        message: `确定要删除歌单"${this.playlist.name}"吗?此操作不可撤销。`,
+        primaryButton: {
+          value: '取消',
+          action: () => {}
+        },
+        secondaryButton: {
+          value: '删除',
+          fontColor: Color.Red,
+          action: async () => {
+            const success = await this.playlistTable.deletePlaylist(this.playlistId)
+            if (success) {
+              ToastUtil.showToast('歌单删除成功')
+              // 发送刷新事件
+              emitter.emit({ eventId: EventConstants.EVENT_PLAYLIST_REFRESH }, {})
+              router.back()
+            } else {
+              ToastUtil.showToast('歌单删除失败')
+            }
+          }
+        }
+      })
+    }
+  }
+
+  /**
+   * 从歌单移除歌曲
+   */
+  async removeSongFromPlaylist(song: VideoItem) {
+    if (this.playlist) {
+      const success = await this.playlistTable.removeSongFromPlaylist(this.playlistId, song.filePath)
+      if (success) {
+        // 从本地列表移除
+        const index = this.songList.findIndex(s => s.filePath === song.filePath)
+        if (index !== -1) {
+          this.songList.splice(index, 1)
+          this.songList = [...this.songList] // 触发UI更新
+        }
+        
+        // 更新歌单信息
+        this.playlist.songCount = this.songList.length
+        await this.playlistTable.updatePlaylist(this.playlist.id, this.playlist.name, this.playlist.description)
+        
+        ToastUtil.showToast('已从歌单移除')
+      } else {
+        ToastUtil.showToast('移除失败')
+      }
+    }
+  }
+
+  build() {
+    Column() {
+      // 顶部安全区和标题栏
+      Column() {
+        Blank()
+          .height(px2vp(AppUtil.getStatusBarHeight()))
+          .backgroundColor($r('app.color.title_bar_bg'))
+          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
+        
+        // 标题栏
+        Row() {
+          Image($r('app.media.back'))
+            .width(24)
+            .height(24)
+            .margin({ left: 12, right: 8 })
+            .onClick(() => {
+              // 如果在排序模式,先退出排序模式
+              if (this.isSortMode) {
+                this.exitSortMode()
+              } else {
+                router.back()
+              }
+            })
+
+          Text(this.isSortMode ? '排序模式' : (this.playlist?.name || '歌单详情'))
+            .fontSize(18)
+            .fontColor(Color.White)
+            .fontWeight(FontWeight.Medium)
+            .layoutWeight(1)
+            .textAlign(TextAlign.Center)
+
+          // 排序/完成按钮
+          Text(this.isSortMode ? '完成' : '排序')
+            .fontSize(16)
+            .fontColor(Color.White)
+            .margin({ right: 12 })
+            .onClick(() => {
+              if (this.isSortMode) {
+                this.exitSortMode()
+              } else {
+                this.enterSortMode()
+              }
+            })
+        }
+        .height(48)
+        .width('100%')
+        .alignItems(VerticalAlign.Center)
+        .backgroundColor($r('app.color.title_bar_bg'))
+      }
+
+      if (this.isLoading) {
+        // 加载状态
+        Column() {
+          LoadingProgress()
+            .width(40)
+            .height(40)
+            .color(this.themeColor )
+          
+          Text('加载中...')
+            .fontSize(14)
+            .fontColor($r('app.color.text_color'))
+            .margin({ top: 12 })
+        }
+        .layoutWeight(1)
+        .justifyContent(FlexAlign.Center)
+      } else if (this.playlist) {
+        // 歌单信息区域 - 参考LocalMusic的封面标题区域设计
+        Column() {
+          // 背景区域
+          Row() {
+            Column() {
+              // 歌单封面
+              Image(this.playlist.coverPath || $r('app.media.hm_playlist'))
+                .width(100)
+                .height(100)
+                .borderRadius(12)
+                .clip(true)
+                .interpolation(ImageInterpolation.High)
+                .autoResize(true)
+                .shadow({
+                  radius: 15,
+                  color: '#0000001a',
+                  offsetX: 0,
+                  offsetY: 6
+                })
+            }
+            .alignItems(HorizontalAlign.Start)
+            .margin({ left: 24, top: 16, bottom: 16 })
+
+            Column() {
+              // 歌单名称
+              Text(this.playlist.name)
+                .fontSize(20)
+                .fontWeight(FontWeight.Bold)
+                .fontColor($r('app.color.text_color'))
+                .maxLines(2)
+                .textOverflow({ overflow: TextOverflow.Ellipsis })
+                .margin({ bottom: 8 })
+
+              // 歌单描述
+              if (this.playlist.description) {
+                Text(this.playlist.description)
+                  .fontSize(14)
+                  .fontColor($r('app.color.text_color'))
+                  .opacity(0.8)
+                  .maxLines(2)
+                  .textOverflow({ overflow: TextOverflow.Ellipsis })
+                  .margin({ bottom: 12 })
+                  .lineHeight(18)
+              }
+
+              // 歌单统计信息
+              Row() {
+                Text(`共${this.playlist.songCount}首歌`)
+                  .fontSize(13)
+                  .fontWeight(FontWeight.Bold)
+                  .fontColor($r('app.color.text_color'))
+                  .opacity(0.8)
+
+                if (this.calculateTotalDuration() > 0) {
+                  Text(` · ${this.formatTotalDuration(this.calculateTotalDuration())}`)
+                    .fontSize(13)
+                    .fontColor($r('app.color.text_color'))
+                    .opacity(0.7)
+                    .margin({ left: 4 })
+                }
+              }
+              .margin({ top: 8 })
+            }
+            .height('100%')
+            .layoutWeight(1)
+            .margin({ left: 16 })
+            .justifyContent(FlexAlign.Center)
+            .alignItems(HorizontalAlign.Start)
+          }
+          .width('100%')
+          .height(140)
+          .justifyContent(FlexAlign.SpaceBetween)
+          .padding({ right: 24 })
+
+          // 操作按钮区域
+          Row({ space: 12 }) {
+            // 播放全部按钮 - 参考LocalMusic的按钮样式
+            Button() {
+              Row({ space: 8 }) {
+                Image($r('app.media.ic_play'))
+                  .width(16)
+                  .height(16)
+                  .fillColor(Color.White)
+
+                Text('播放全部')
+                  .fontSize(14)
+                  .fontColor(Color.White)
+                  .fontWeight(FontWeight.Medium)
+              }
+              .justifyContent(FlexAlign.Center)
+            }
+            .layoutWeight(1)
+            .height(44)
+            .backgroundColor(this.themeColor )
+            .borderRadius(22)
+            .shadow({
+              radius: 8,
+              color: '#0a59f740',
+              offsetX: 0,
+              offsetY: 4
+            })
+            .onClick(() => {
+              this.playPlaylist()
+            })
+            .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
+            })
+
+            // 编辑歌单按钮
+            Button() {
+              Row({ space: 6 }) {
+                Text('✏️')
+                  .fontSize(16)
+                  .fontColor(this.themeColor )
+
+                Text('编辑')
+                  .fontSize(14)
+                  .fontColor(this.themeColor )
+                  .fontWeight(FontWeight.Medium)
+              }
+              .justifyContent(FlexAlign.Center)
+            }
+            .layoutWeight(1)
+            .height(44)
+            .backgroundColor(Color.Transparent)
+            .borderRadius(22)
+            .border({
+              width: 1.5,
+              color: this.themeColor
+            })
+            .onClick(() => {
+              this.editPlaylist()
+            })
+            .stateStyles({
+              normal: {
+                .backgroundColor(Color.Transparent)
+                .scale({ x: 1, y: 1 })
+              },
+              pressed: {
+                .backgroundColor('#0a59f715')
+                .scale({ x: 0.96, y: 0.96 })
+              }
+            })
+            .animation({
+              duration: 150,
+              curve: Curve.EaseInOut
+            })
+
+            // 删除按钮
+            Button() {
+              Row({ space: 6 }) {
+                Text('🗑️')
+                  .fontSize(16)
+
+                Text('删除')
+                  .fontSize(14)
+                  .fontColor(Color.Red)
+                  .fontWeight(FontWeight.Medium)
+              }
+              .justifyContent(FlexAlign.Center)
+            }
+            .layoutWeight(1)
+            .height(44)
+            .backgroundColor(Color.Transparent)
+            .borderRadius(22)
+            .border({
+              width: 1.5,
+              color: Color.Red
+            })
+            .onClick(() => {
+              this.deletePlaylist()
+            })
+            .stateStyles({
+              normal: {
+                .backgroundColor(Color.Transparent)
+                .scale({ x: 1, y: 1 })
+              },
+              pressed: {
+                .backgroundColor('#ff000015')
+                .scale({ x: 0.96, y: 0.96 })
+              }
+            })
+            .animation({
+              duration: 150,
+              curve: Curve.EaseInOut
+            })
+          }
+          .width('100%')
+          .padding({ left: 24, right: 24, bottom: 20 })
+          .justifyContent(FlexAlign.Start)
+        }
+        .backgroundColor($r('app.color.bg_card'))
+        .margin({ left: 16, right: 16, top: 16 })
+        .borderRadius(16)
+        .shadow({
+          radius: 12,
+          color: '#00000014',
+          offsetX: 0,
+          offsetY: 4
+        })
+        .scale({ x: this.contentScale, y: this.contentScale })
+        .opacity(this.pageOpacity)
+        .transition(TransitionEffect.OPACITY.animation({ duration: 600, curve: Curve.EaseOut }))
+        .transition(TransitionEffect.scale({ x: 0.95, y: 0.95 }).animation({ duration: 600, curve: Curve.EaseOut }))
+
+        // 歌曲列表标题 - 简化设计,与LocalMusic保持一致
+        if (this.songList.length > 0) {
+          Row() {
+            Text('歌曲列表')
+              .fontSize(16)
+              .fontWeight(FontWeight.Medium)
+              .fontColor($r('app.color.text_color'))
+              .opacity(0.9)
+              .layoutWeight(1)
+
+            Text(`${this.songList.length}首`)
+              .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 })
+          .opacity(this.pageOpacity)
+          .transition(TransitionEffect.OPACITY.animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
+        }
+
+        // 歌曲列表 - 参考LocalMusic的MusicItem样式
+        if (this.songList.length > 0) {
+          List({ space: 0 }) {
+            ForEach(this.songList, (song: VideoItem, index: number) => {
+              ListItem() {
+                Button({ type: ButtonType.Normal, stateEffect: true }) {
+                  Row() {
+                    // 歌曲封面 - 改为圆形
+                    Stack() {
+                      Image(song.pixelMapPath || $r('app.media.music_red'))
+                        .width(48)
+                        .height(48)
+                        .borderRadius(24) // 改为圆形
+                        .objectFit(ImageFit.Cover)
+                        .interpolation(ImageInterpolation.High)
+                        .autoResize(true)
+                        .margin({ left: 16 })
+                        .onClick(() => {
+                          this.playSong(song, index)
+                        })
+                    }
+                    .width(64)
+
+                    // 歌曲信息 - 参考LocalMusic的布局
+                    Column() {
+                      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() {
+                          Text(song.artist)
+                            .fontSize(13)
+                            .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)
+                          Text(song.album)
+                            .fontSize(13)
+                            .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.duration && typeof song.duration === 'number' && song.duration > 0) {
+                          Text(this.formatDuration(song.duration))
+                            .fontSize(13)
+                            .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 })
+                        }
+                      }
+                      .width('100%')
+                      .margin({ left: 12, top: 4 })
+                      .alignItems(VerticalAlign.Center)
+                    }
+                    .layoutWeight(1)
+                    .alignItems(HorizontalAlign.Start)
+                    .justifyContent(FlexAlign.Center)
+
+
+                    // 排序模式下显示上下移动按钮,否则显示更多按钮
+                    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 })
+                    } else {
+                      Text('⋯')
+                        .fontSize(20)
+                        .fontColor($r('app.color.text_color'))
+                        .opacity(0.6)
+                        .margin({ right: 15 })
+                        .onClick(() => {
+                          this.showSongMenu(song)
+                        })
+                    }
+                  }
+                  .width('100%')
+                  .height(70)
+                  .justifyContent(FlexAlign.Start)
+                  .alignItems(VerticalAlign.Center)
+                }
+                .backgroundColor(Color.Transparent)
+                .height(70)
+                .width('100%')
+                .onClick(() => {
+                  // 排序模式下禁用点击播放
+                  if (!this.isSortMode) {
+                    this.playSong(song, index)
+                  }
+                })
+                .gesture(
+                  LongPressGesture()
+                    .onAction(() => {
+                      // 排序模式下不显示菜单
+                      if (!this.isSortMode) {
+                        this.showSongMenu(song)
+                      }
+                    })
+                )
+                .stateStyles({
+                  normal: {
+                    .backgroundColor(Color.Transparent)
+                  },
+                  pressed: {
+                    .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: this.themeColor 
+                })
+                .opacity(this.pageOpacity)
+                .translate({ x: 0, y: this.showContent ? 0 : 20 })
+                .transition(TransitionEffect.OPACITY.animation({
+                  duration: 600,
+                  curve: Curve.EaseOut,
+                  delay: 300 + index * 30
+                }))
+                .transition(TransitionEffect.translate({ y: 20 }).animation({
+                  duration: 600,
+                  curve: Curve.EaseOut,
+                  delay: 300 + index * 30
+                }))
+              }
+            })
+          }
+          .width('100%')
+          .layoutWeight(1)
+          .backgroundColor($r('app.color.bg_card'))
+          .margin({ left: 16, right: 16 })
+          .borderRadius(12)
+          .divider({
+            strokeWidth: 0.5,
+            color: '#0000001a',
+            startMargin: 80,
+            endMargin: 20
+          })
+          .opacity(this.pageOpacity)
+          .scale({ x: this.contentScale, y: this.contentScale })
+          .transition(TransitionEffect.OPACITY.animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
+          .transition(TransitionEffect.scale({ x: 0.95, y: 0.95 }).animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
+        } else {
+          // 空状态
+          Column() {
+            // 空状态容器
+            Column() {
+              // 空状态图标容器
+              Stack() {
+                // 背景圆圈
+                Circle({ width: 160, height: 160 })
+                  .fill('#0a59f715')
+                  .border({
+                    width: 2,
+                    color: '#0a59f71a'
+                  })
+
+                // 中间圆圈
+                Circle({ width: 120, height: 120 })
+                  .fill('#0a59f722')
+
+                // 音符图标
+                Image($r('app.media.music_red'))
+                  .width(64)
+                  .height(64)
+                  .opacity(0.6)
+                  .fillColor(this.themeColor )
+              }
+              .margin({ bottom: 32 })
+
+              // 空状态标题
+              Text('歌单还是空的')
+                .fontSize(22)
+                .fontColor($r('app.color.text_color'))
+                .fontWeight(FontWeight.Bold)
+                .margin({ bottom: 12 })
+                .letterSpacing(0.5)
+
+              // 空状态描述
+              Text('快来添加你喜欢的音乐吧\n让这个歌单充满美妙的旋律')
+                .fontSize(15)
+                .fontColor($r('app.color.text_color'))
+                .opacity(0.8)
+                .margin({ bottom: 40 })
+                .textAlign(TextAlign.Center)
+                .lineHeight(24)
+                .maxLines(2)
+
+              // 添加歌曲按钮
+              Button() {
+                Row({ space: 8 }) {
+                  Text('+')
+                    .fontSize(20)
+                    .fontColor(Color.White)
+
+                  Text('添加歌曲')
+                    .fontSize(16)
+                    .fontColor(Color.White)
+                    .fontWeight(FontWeight.Medium)
+                }
+                .justifyContent(FlexAlign.Center)
+              }
+              .width(160)
+              .height(48)
+              .backgroundColor(this.themeColor )
+              .borderRadius(24)
+              .shadow({
+                radius: 12,
+                color: '#0a59f74d',
+                offsetX: 0,
+                offsetY: 6
+              })
+              .onClick(() => {
+                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('添加歌曲失败')
+                    }
+                  }
+                )
+              })
+              .stateStyles({
+                normal: {
+                  .backgroundColor(this.themeColor )
+                  .scale({ x: 1, y: 1 })
+                },
+                pressed: {
+                  .backgroundColor('#0a59f7cc')
+                  .scale({ x: 0.96, y: 0.96 })
+                }
+              })
+              .animation({
+                duration: 200,
+                curve: Curve.EaseInOut
+              })
+
+              // 快速操作提示
+              Text('或长按歌单选择更多操作')
+                .fontSize(13)
+                .fontColor($r('app.color.text_color'))
+                .margin({ top: 16 })
+                .opacity(0.7)
+            }
+            .width('100%')
+            .padding(32)
+            .backgroundColor($r('app.color.bg_card'))
+            .borderRadius(20)
+            .margin({ left: 16, right: 16 })
+            .shadow({
+              radius: 16,
+              color: '#0000000f',
+              offsetX: 0,
+              offsetY: 8
+            })
+            .opacity(this.pageOpacity)
+            .scale({ x: this.contentScale, y: this.contentScale })
+            .transition(TransitionEffect.OPACITY.animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
+            .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 })
+        }
+      }
+    }
+    .width('100%')
+    .height('100%')
+    .backgroundColor($r('app.color.index_background'))
+  }
+
+  /**
+   * 显示更多菜单
+   */
+  showMoreMenu() {
+    // 简单直接的二选一,避免复杂的多级菜单
+    AlertDialog.show({
+      title: '歌单操作',
+      message: '🎵 添加歌曲:向歌单添加新音乐\n✏️ 编辑歌单:修改歌单信息\n\n点击"确定"添加歌曲,点击"取消"编辑歌单',
+      primaryButton: {
+        value: '取消 (编辑)',
+        action: () => {
+          this.editPlaylist()
+        }
+      },
+      secondaryButton: {
+        value: '确定 (添加)',
+        action: () => {
+          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('添加歌曲失败')
+          }
+        }
+      )
+    }
+  }
+
+  /**
+   * 显示歌曲菜单
+   */
+  showSongMenu(song: VideoItem) {
+    AlertDialog.show({
+      title: song.name,
+      message: '',
+      primaryButton: {
+        value: '取消',
+        action: () => {}
+      },
+      secondaryButton: {
+        value: '从歌单移除',
+        fontColor: Color.Red,
+        action: () => {
+          this.removeSongFromPlaylist(song)
+        }
+      }
+    })
+  }
+
+  /**
+   * 进入排序模式
+   */
+  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 { VideoItem } from '../viewmodel/VideoItem'
 import Logger from '../common/util/Logger'
 import Logger from '../common/util/Logger'
 import { CommonConstants, STR_LOCK_VIDEO } from '../common/constants/CommonConstants'
 import { CommonConstants, STR_LOCK_VIDEO } from '../common/constants/CommonConstants'
+import { EventConstants } from '../common/constants/EventConstants'
 import { BusinessError, emitter } from '@kit.BasicServicesKit'
 import { BusinessError, emitter } from '@kit.BasicServicesKit'
 import { BreakpointTypeEnum } from '../common/util/BreakpointSystem'
 import { BreakpointTypeEnum } from '../common/util/BreakpointSystem'
 import { resourceManager } from '@kit.LocalizationKit'
 import { resourceManager } from '@kit.LocalizationKit'
@@ -102,7 +103,7 @@ export struct ScanFilePage{
 
 
   endScan(isOnekey:boolean){
   endScan(isOnekey:boolean){
     const eventData: emitter.EventData = {};
     const eventData: emitter.EventData = {};
-    emitter.emit({ eventId: 101 }, eventData); // 发送视频打开广播事件
+    emitter.emit({ eventId: EventConstants.EVENT_SCAN_UPDATE }, eventData); // 发送视频打开广播事件
     this.watchStatus(false)
     this.watchStatus(false)
     this.currentFilePath = '扫描文件入库成功!'
     this.currentFilePath = '扫描文件入库成功!'
     if(isOnekey){
     if(isOnekey){

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

@@ -1,6 +1,7 @@
 import TitleBar from '../view/TitleBar'
 import TitleBar from '../view/TitleBar'
 import { promptAction, router } from '@kit.ArkUI'
 import { promptAction, router } from '@kit.ArkUI'
 import { CommonConstants } from '../common/constants/CommonConstants'
 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 { AppUtil, DeviceUtil, DisplayUtil, FileUtil, MD5, PreferencesUtil, ToastUtil } from '@pura/harmony-utils'
 import { CustomContentDialog } from '@kit.ArkUI'
 import { CustomContentDialog } from '@kit.ArkUI'
 import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
 import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
@@ -2030,7 +2031,7 @@ export struct SettingPage {
   //发送广播通知更新UI
   //发送广播通知更新UI
   sendChangeEvent() {
   sendChangeEvent() {
     const eventData: emitter.EventData = {};
     const eventData: emitter.EventData = {};
-    emitter.emit({ eventId: 333 }, eventData); // 发送广播通知更新doSwipBack
+    emitter.emit({ eventId: EventConstants.EVENT_SETTING_UPDATE }, eventData); // 发送广播通知更新doSwipBack
   }
   }
 
 
   goSelectImage() {
   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 { authentication } from '@kit.AccountKit';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
+import { EventConstants } from '../common/constants/EventConstants';
 import { util } from '@kit.ArkTS';
 import { util } from '@kit.ArkTS';
 import { DialogHelper } from '@pura/harmony-dialog';
 import { DialogHelper } from '@pura/harmony-dialog';
 import { Pay } from '@cashier_alipay/cashiersdk';
 import { Pay } from '@cashier_alipay/cashiersdk';
@@ -219,7 +220,7 @@ export struct UserCenter {
       }
       }
     }
     }
     await this.fetchUserInfo();
     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();
     await this.fetchUserInfo();
-    emitter.emit({ eventId: 1001 }, {})
+    emitter.emit({ eventId: EventConstants.EVENT_USER_STATE_CHANGE }, {})
   }
   }
 
 
   @Builder
   @Builder
@@ -562,7 +563,7 @@ export struct UserCenter {
                 this.subscriptionEndDate = '';
                 this.subscriptionEndDate = '';
                 this.isForever=false;
                 this.isForever=false;
                 UserUtil.logout();
                 UserUtil.logout();
-                emitter.emit({ eventId: 1001 }, {})
+                emitter.emit({ eventId: EventConstants.EVENT_USER_STATE_CHANGE }, {})
                 ToastUtil.showToast('已退出登录')
                 ToastUtil.showToast('已退出登录')
               })
               })
           } else {
           } else {

+ 311 - 205
entry/src/main/ets/view/LocalMusic.ets

@@ -1,7 +1,7 @@
 import TitleBar from './TitleBar'
 import TitleBar from './TitleBar'
 import { curves, display, PiPWindow, promptAction, router, SymbolGlyphModifier, window } from '@kit.ArkUI';
 import { curves, display, PiPWindow, promptAction, router, SymbolGlyphModifier, window } from '@kit.ArkUI';
 import { VideoItem } from '../viewmodel/VideoItem';
 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 {  LengthMetrics, SegmentButton,borderRadiuses, SegmentButtonOptions } from '@kit.ArkUI';
 import {
 import {
   AppUtil,
   AppUtil,
@@ -22,6 +22,7 @@ import {
 import { imagePathToPixelMap } from '../common/util/CommUtils';
 import { imagePathToPixelMap } from '../common/util/CommUtils';
 import { unifiedDataChannel, uniformDataStruct, uniformTypeDescriptor } from '@kit.ArkData';
 import { unifiedDataChannel, uniformDataStruct, uniformTypeDescriptor } from '@kit.ArkData';
 import { CommonConstants } from '../common/constants/CommonConstants';
 import { CommonConstants } from '../common/constants/CommonConstants';
+import { EventConstants } from '../common/constants/EventConstants';
 import Logger from '../common/util/Logger';
 import Logger from '../common/util/Logger';
 import { photoAccessHelper } from '@kit.MediaLibraryKit';
 import { photoAccessHelper } from '@kit.MediaLibraryKit';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
@@ -82,13 +83,27 @@ import { TextNodeController } from './PipLyricTextBuilder';
 import { IndexerView } from './IndexerView';
 import { IndexerView } from './IndexerView';
 import { KeyCode } from '@kit.InputKit';
 import { KeyCode } from '@kit.InputKit';
 import { KnockController } from '../controller/KnockController';
 import { KnockController } from '../controller/KnockController';
-import { DeleteComptent } from '../view/DeleteComptent'
+import { DeleteComptent } from '../view/DeleteComptent';
 import { ABLoopComptent } from '../view/ABLoopComptent';
 import { ABLoopComptent } from '../view/ABLoopComptent';
 import { FixMessyView } from '../view/FixMessyView';
 import { FixMessyView } from '../view/FixMessyView';
 import { parseCueFile,CueTrack,CueInfo } from '../common/util/CueUtils';
 import { parseCueFile,CueTrack,CueInfo } from '../common/util/CueUtils';
 import { CueComptent } from '../view/CueComptent';
 import { CueComptent } from '../view/CueComptent';
+import PlaylistTable from '../common/util/PlaylistTable';
+import { showAddToPlaylistDialog } from '../dialog/AddToPlaylistDialog';
+import { showCreatePlaylistDialog } from '../dialog/PlaylistDialog';
 const TAG = 'LocalMusic';
 const TAG = 'LocalMusic';
 
 
+/**
+ * 歌单播放事件数据
+ */
+interface PlaylistEventData {
+  playlistId: string;
+  playlistName: string;
+  songCount: number;
+  startIndex: number;
+  songFilePaths: string[];
+}
+
 const DEFAULT_INDEX =
 const DEFAULT_INDEX =
   ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
   ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
     'X', 'Y', 'Z']
     'X', 'Y', 'Z']
@@ -147,10 +162,9 @@ const ITEM_HEIGHT_BIG: number = 78; // 列表项大高度
 @Preview
 @Preview
 @Component
 @Component
 export struct LocalMusic {
 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 mediaKuCount: number = 0;
   @State albumCount: number = 0;
   @State albumCount: number = 0;
   @State artistCount: number = 0;
   @State artistCount: number = 0;
@@ -179,6 +193,8 @@ export struct LocalMusic {
   @Consume isHistory: boolean
   @Consume isHistory: boolean
   static readonly HISTORY_MUSIC: string = 'music_historyList';
   static readonly HISTORY_MUSIC: string = 'music_historyList';
   private table: MediaTable = new MediaTable(getContext(this))
   private table: MediaTable = new MediaTable(getContext(this))
+  private playlistTable: PlaylistTable | null = null
+  @State allPlaylists: Playlist[] = []
   @State isZero: boolean = false
   @State isZero: boolean = false
   @State fileList: Array<string> = []
   @State fileList: Array<string> = []
   @State dirList: Array<VideoItem> = []
   @State dirList: Array<VideoItem> = []
@@ -279,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() {
   onModeChange() {
     this.isFavMusic = false
     this.isFavMusic = false
     if (this.modeType === 2 || this.modeType === 3) {
     if (this.modeType === 2 || this.modeType === 3) {
@@ -287,6 +314,7 @@ export struct LocalMusic {
       this.titleBarModel.setRightIcon(($r('app.media.add')))
       this.titleBarModel.setRightIcon(($r('app.media.add')))
     }
     }
     LogUtil.info('onecold onModeChange = ' + this.modeType)
     LogUtil.info('onecold onModeChange = ' + this.modeType)
+    this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
     switch (this.modeType) {
     switch (this.modeType) {
       case 0:
       case 0:
         this.getSortedFiles(this.currentPath)
         this.getSortedFiles(this.currentPath)
@@ -307,6 +335,12 @@ export struct LocalMusic {
         this.updateListData(this.albumList)
         this.updateListData(this.albumList)
         this.titleBarModel.setTitleName(Utility.resourceToString(this.context, $r('app.string.album')))
         this.titleBarModel.setTitleName(Utility.resourceToString(this.context, $r('app.string.album')))
         break
         break
+      case 4://歌单
+        this.titleBarModel.setLeftIconMainNoAnimate($r('app.media.menu'))
+        this.updateListData(this.currentSongList, true)
+        this.titleBarModel.setTitleName(this.currentSongListName)
+        this.isShowTitleBar = false
+        break
     }
     }
   }
   }
 
 
@@ -506,6 +540,9 @@ export struct LocalMusic {
 
 
   // 组件生命周期
   // 组件生命周期
   aboutToAppear() {
   aboutToAppear() {
+    // 初始化 PlaylistTable
+    this.playlistTable = new PlaylistTable(getContext(this))
+
     this.sonTwoFingerType = PreferencesUtil.getNumberSync('sonTwoFingerType', 3);
     this.sonTwoFingerType = PreferencesUtil.getNumberSync('sonTwoFingerType', 3);
     this.columns = PreferencesUtil.getNumberSync('lastColumns', 2);
     this.columns = PreferencesUtil.getNumberSync('lastColumns', 2);
     if(PreferencesUtil.getBooleanSync('isFirstTiped',true)){
     if(PreferencesUtil.getBooleanSync('isFirstTiped',true)){
@@ -529,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) => {
     emitter.on(eventMusic, (eventData: emitter.EventData) => {
       this.saveVideoDatas([eventData.data?.message], true)
       this.saveVideoDatas([eventData.data?.message], true)
@@ -537,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) => {
     emitter.on(eventUpdateSwipeBack, (eventData: emitter.EventData) => {
 
 
@@ -548,13 +585,50 @@ export struct LocalMusic {
     });
     });
 
 
     //ScanFilePage广播接收时间
     //ScanFilePage广播接收时间
-    let eventScanUpdate: emitter.InnerEvent = { eventId: 101 }
+    let eventScanUpdate: emitter.InnerEvent = { eventId: EventConstants.EVENT_SCAN_UPDATE }
     // 监听ScanFilePage广播事件(更新数据库)
     // 监听ScanFilePage广播事件(更新数据库)
     emitter.on(eventScanUpdate, (eventData: emitter.EventData) => {
     emitter.on(eventScanUpdate, (eventData: emitter.EventData) => {
       this.doUpdateData()
       this.doUpdateData()
     });
     });
 
 
-    let eventSetting: emitter.InnerEvent = { eventId: 333 }
+    // 监听歌单播放请求事件
+    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))
+
+      const data = eventData.data as Record<string, Object>
+
+      if (!data) {
+        Logger.error('heanup eventPlaylistPlay: eventData.data is undefined or null')
+        return
+      }
+
+      // 检查歌单播放数据结构
+      if (data.playlistId && data.songFilePaths && data.startIndex !== undefined) {
+        Logger.info('heanup eventPlaylistPlay: 接收到歌单播放数据')
+        // 手动构建数据对象以避免类型转换问题
+        const playlistData: PlaylistEventData = {
+          playlistId: data.playlistId as string,
+          playlistName: data.playlistName as string,
+          songCount: data.songCount as number,
+          startIndex: data.startIndex as number,
+          songFilePaths: data.songFilePaths as string[]
+        }
+        // 根据文件路径重新构建歌曲列表
+        this.handlePlaylistPlayRequest(
+          playlistData.playlistId,
+          playlistData.playlistName,
+          playlistData.songFilePaths,
+          playlistData.startIndex
+        )
+        return
+      }
+
+      Logger.error('heanup eventPlaylistPlay: 接收到无效的数据结构')
+    });
+
+    let eventSetting: emitter.InnerEvent = { eventId: EventConstants.EVENT_SETTING_UPDATE }
     // 监听广播事件(通用设置配置更新)
     // 监听广播事件(通用设置配置更新)
     emitter.on(eventSetting, (eventData: emitter.EventData) => {
     emitter.on(eventSetting, (eventData: emitter.EventData) => {
       this.doChangeSetting()
       this.doChangeSetting()
@@ -704,7 +778,6 @@ export struct LocalMusic {
     const context = getContext(this) as common.UIAbilityContext
     const context = getContext(this) as common.UIAbilityContext
     context.getApplicationContext().setColorMode(colorMode)
     context.getApplicationContext().setColorMode(colorMode)
   }
   }
-
   initSetting() {
   initSetting() {
     this.sortType = PreferencesUtil.getNumberSync(SettingPage.SORT_TYPE, 4)
     this.sortType = PreferencesUtil.getNumberSync(SettingPage.SORT_TYPE, 4)
     this.isCustomizeBg = PreferencesUtil.getBooleanSync(SettingPage.IS_CUSTOMIZE_BG, false)
     this.isCustomizeBg = PreferencesUtil.getBooleanSync(SettingPage.IS_CUSTOMIZE_BG, false)
@@ -794,7 +867,6 @@ export struct LocalMusic {
       this.setBarHeightNormal()
       this.setBarHeightNormal()
     }
     }
   }
   }
-
   onPageShow() {
   onPageShow() {
     this.knockController?.immersiveListening();
     this.knockController?.immersiveListening();
     app.setImageCacheCount(100);
     app.setImageCacheCount(100);
@@ -802,7 +874,6 @@ export struct LocalMusic {
     app.setImageRawDataCacheSize(104857600);
     app.setImageRawDataCacheSize(104857600);
     Logger.info('onecold onPageShow currentBreakpoint= ' + this.currentBreakpoint)
     Logger.info('onecold onPageShow currentBreakpoint= ' + this.currentBreakpoint)
   }
   }
-
   //开启线程查看各个数据库
   //开启线程查看各个数据库
   makeWorker() {
   makeWorker() {
     setTimeout(() => {
     setTimeout(() => {
@@ -837,7 +908,6 @@ export struct LocalMusic {
           this.doUpdateData()
           this.doUpdateData()
           break;
           break;
         case 102: //查询媒体库列表
         case 102: //查询媒体库列表
-          this.isRefreshing = false
           this.mediaKuList = e.data.data
           this.mediaKuList = e.data.data
           Logger.info('onecold 查询媒体库列表 this.mediaKuList length= ' + this.mediaKuList.length)
           Logger.info('onecold 查询媒体库列表 this.mediaKuList length= ' + this.mediaKuList.length)
           Utility.doSortListAscending(this.mediaKuList)
           Utility.doSortListAscending(this.mediaKuList)
@@ -856,7 +926,6 @@ export struct LocalMusic {
           }
           }
           break;
           break;
         case 103: //收到查询艺术家列表
         case 103: //收到查询艺术家列表
-          this.isRefreshing = false
           this.artistMap = e.data.data1;
           this.artistMap = e.data.data1;
           this.artistList = e.data.data2;
           this.artistList = e.data.data2;
           PreferencesUtil.putSync('artistCount', this.artistList.length)
           PreferencesUtil.putSync('artistCount', this.artistList.length)
@@ -869,17 +938,15 @@ export struct LocalMusic {
           } else {
           } else {
             PreferencesUtil.putSync('artistList',  JSON.stringify(this.artistList));
             PreferencesUtil.putSync('artistList',  JSON.stringify(this.artistList));
           }
           }
-          // 处理并缓存前80条艺术家数据
+          // 处理并缓存前60条艺术家数据
           const sortedEntries = Array.from(this.artistMap.entries())
           const sortedEntries = Array.from(this.artistMap.entries())
             .sort((a, b) => b[1].length - a[1].length)
             .sort((a, b) => b[1].length - a[1].length)
-            .slice(0, 80);
+            .slice(0, 60);
 
 
           PreferencesUtil.putSync('artistMap',  JSON.stringify(sortedEntries));
           PreferencesUtil.putSync('artistMap',  JSON.stringify(sortedEntries));
-
           break;
           break;
 
 
         case 104: //收到查询专辑列表
         case 104: //收到查询专辑列表
-          this.isRefreshing = false
           this.albumMap = e.data.data1;
           this.albumMap = e.data.data1;
 
 
           this.albumList = e.data.data2
           this.albumList = e.data.data2
@@ -900,7 +967,7 @@ export struct LocalMusic {
           }
           }
           const mapArrayAlbum = Array.from(this.albumMap.entries())
           const mapArrayAlbum = Array.from(this.albumMap.entries())
             .sort((a, b) => b[1].length - a[1].length)
             .sort((a, b) => b[1].length - a[1].length)
-            .slice(0, 80);
+            .slice(0, 60);
           PreferencesUtil.putSync('albumMap',  JSON.stringify(mapArrayAlbum));
           PreferencesUtil.putSync('albumMap',  JSON.stringify(mapArrayAlbum));
           break;
           break;
 
 
@@ -1023,10 +1090,10 @@ export struct LocalMusic {
     console.info('LifeCycleComponent aboutToDisappear');
     console.info('LifeCycleComponent aboutToDisappear');
     this.knockController?.immersiveDisableListening();
     this.knockController?.immersiveDisableListening();
     this.curIndex = 0
     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.mDestroyPage = true;
     this.mIjkMediaPlayer.setScreenOnWhilePlaying(false);
     this.mIjkMediaPlayer.setScreenOnWhilePlaying(false);
     if (this.CONTROL_PlayStatus != PlayStatus.INIT) {
     if (this.CONTROL_PlayStatus != PlayStatus.INIT) {
@@ -1321,7 +1388,7 @@ export struct LocalMusic {
           this.titleBarModel.setTitleName(Utility.resourceToString(this.context, $r('app.string.album')))
           this.titleBarModel.setTitleName(Utility.resourceToString(this.context, $r('app.string.album')))
           break
           break
         case 4: //同步数据
         case 4: //同步数据
-          this.asyncCurrentPathData()
+          // this.asyncCurrentPathData()
           break
           break
       }
       }
 
 
@@ -1458,7 +1525,6 @@ export struct LocalMusic {
       }
       }
     })
     })
   }
   }
-
   doSortType(index: number) {
   doSortType(index: number) {
     switch (index) {
     switch (index) {
       case 0:
       case 0:
@@ -1600,7 +1666,6 @@ export struct LocalMusic {
       }
       }
     })
     })
   }
   }
-
   //拉起音频
   //拉起音频
   goSelectMusic() {
   goSelectMusic() {
     if (DeviceUtil.getDeviceType() == resourceManager.DeviceType.DEVICE_TYPE_TABLET ||
     if (DeviceUtil.getDeviceType() == resourceManager.DeviceType.DEVICE_TYPE_TABLET ||
@@ -1935,7 +2000,6 @@ export struct LocalMusic {
         if (!newPath.includes(this.lockPath)) { //如果移动到私密,就不插入数据库
         if (!newPath.includes(this.lockPath)) { //如果移动到私密,就不插入数据库
           this.table.insert(newItem, (id: number) => {
           this.table.insert(newItem, (id: number) => {
             //加入数据库
             //加入数据库
-
           });
           });
         }
         }
 
 
@@ -2099,7 +2163,6 @@ export struct LocalMusic {
 
 
 
 
   }
   }
-
   build() {
   build() {
 
 
     Scroll() {
     Scroll() {
@@ -2205,22 +2268,7 @@ export struct LocalMusic {
                 if(this.isGridMusic){
                 if(this.isGridMusic){
                   this.getGridView()
                   this.getGridView()
                 }else {
                 }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) {
               if (this.modeType == 0) {
@@ -2403,7 +2451,6 @@ export struct LocalMusic {
     }
     }
 
 
   }
   }
-
   @Builder
   @Builder
   TagsContentCoverBuilder() {
   TagsContentCoverBuilder() {
     Scroll() {
     Scroll() {
@@ -2595,6 +2642,61 @@ export struct LocalMusic {
     }
     }
   }
   }
 
 
+  /**
+   * 加载所有歌单
+   */
+  async loadAllPlaylists() {
+    try {
+      this.allPlaylists = await this.playlistTable?.queryAllPlaylists()!
+      LogUtil.info('heanup Loaded playlists: ' + this.allPlaylists.length)
+    } catch (error) {
+      LogUtil.error('heaunp Failed to load playlists: ' + error.message)
+    }
+  }
+
+  /**
+   * 显示添加到歌单对话框
+   */
+  async showAddToPlaylistDialog(item: VideoItem) {
+    // 加载最新的歌单列表
+    await this.loadAllPlaylists()
+
+    showAddToPlaylistDialog(
+      item,
+      this.allPlaylists,
+      async (playlistId: string) => {
+        // 添加歌曲到歌单
+        const success = await this.playlistTable?.addSongToPlaylist(playlistId, item.filePath)
+        if (success) {
+          ToastUtil.showToast('已添加到歌单')
+          // 重新加载歌单列表
+          await this.loadAllPlaylists()
+        } else {
+          ToastUtil.showToast('歌曲已在该歌单中')
+        }
+      },
+      () => {
+        LogUtil.info('取消添加到歌单')
+      },
+      () => {
+        // 创建新歌单
+        showCreatePlaylistDialog(
+          async (name: string, description: string) => {
+            const success = await this.playlistTable?.createPlaylist(name, description)
+            if (success) {
+              ToastUtil.showToast('歌单创建成功')
+              // 重新加载歌单列表并打开添加对话框
+              await this.loadAllPlaylists()
+              await this.showAddToPlaylistDialog(item)
+            } else {
+              ToastUtil.showToast('歌单创建失败')
+            }
+          }
+        )
+      }
+    )
+  }
+
   doFav(item: VideoItem) {
   doFav(item: VideoItem) {
     LogUtil.info('onecold doFav isFav=' + item.isFav)
     LogUtil.info('onecold doFav isFav=' + item.isFav)
 
 
@@ -2781,9 +2883,7 @@ export struct LocalMusic {
     }
     }
     return true
     return true
   }
   }
-
   @State opacityItem: number = 1; // 控制透明度的状态变量
   @State opacityItem: number = 1; // 控制透明度的状态变量
-
   @Builder
   @Builder
   private MusicItem(item: VideoItem, index?: number) {
   private MusicItem(item: VideoItem, index?: number) {
     Button({ type: ButtonType.Normal, stateEffect: true }) {
     Button({ type: ButtonType.Normal, stateEffect: true }) {
@@ -2981,7 +3081,7 @@ export struct LocalMusic {
 
 
   private readonly tabs: string[] = ['文件夹', '媒体库', '艺术家', '专辑']
   private readonly tabs: string[] = ['文件夹', '媒体库', '艺术家', '专辑']
   @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.tab({
   @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.tab({
-    buttons: [{ text: '文件夹' }, { text: '媒体库' },{ text: '艺术家' },{ text: '专辑' }],
+    buttons: [{ text: '文件夹' }, { text: '媒体库' },{ text: '艺术家' }],
     direction: Direction.Ltr,
     direction: Direction.Ltr,
     buttonPadding:{top:12,bottom:12},
     buttonPadding:{top:12,bottom:12},
     backgroundColor: Color.Transparent,
     backgroundColor: Color.Transparent,
@@ -3106,7 +3206,6 @@ export struct LocalMusic {
     }
     }
     return (this.modeType == 2 || this.modeType == 3) && this.isCanBack
     return (this.modeType == 2 || this.modeType == 3) && this.isCanBack
   }
   }
-
   @Builder
   @Builder
   coverHeader() {
   coverHeader() {
     if (this.isShowCoverHeader()) {
     if (this.isShowCoverHeader()) {
@@ -3265,20 +3364,6 @@ export struct LocalMusic {
 
 
     return this.currentTitleCover
     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
   @Builder
   listViewTitle() {
   listViewTitle() {
     Column() {
     Column() {
@@ -3494,18 +3579,26 @@ export struct LocalMusic {
             this.isSearchMode = true
             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"))
         Image($r("app.media.top_rank"))
           .width(25)
           .width(25)
@@ -3731,7 +3824,7 @@ export struct LocalMusic {
               })
               })
             .transition(TransitionEffect.scale({ x: 0.8, y: 0.8 }).animation({ duration: 500 }))
             .transition(TransitionEffect.scale({ x: 0.8, y: 0.8 }).animation({ duration: 500 }))
             .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 0.8, y: 0.8 }).animation({ duration: 500 }),
             .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 0.8, y: 0.8 }).animation({ duration: 500 }),
-              TransitionEffect.scale({ x: 0, y: 0 })  ))
+              TransitionEffect.scale({ x: 0, y: 0 })))
             .gesture(
             .gesture(
               GestureGroup(GestureMode.Exclusive,
               GestureGroup(GestureMode.Exclusive,
                 SwipeGesture({ direction: SwipeDirection.Horizontal })
                 SwipeGesture({ direction: SwipeDirection.Horizontal })
@@ -3903,7 +3996,6 @@ export struct LocalMusic {
     .scrollBar(BarState.Off)
     .scrollBar(BarState.Off)
 
 
   }
   }
-
   //瀑布流卡片布局
   //瀑布流卡片布局
   @Builder
   @Builder
   private MusicWaterCardItem(item: VideoItem, index: number) {
   private MusicWaterCardItem(item: VideoItem, index: number) {
@@ -4070,8 +4162,6 @@ export struct LocalMusic {
 
 
     })
     })
   }
   }
-
-
   // Grid布局的开始
   // Grid布局的开始
   private dragRefOffSetX: number = 0;
   private dragRefOffSetX: number = 0;
   private dragRefOffSetY: number = 0;
   private dragRefOffSetY: number = 0;
@@ -4090,16 +4180,6 @@ export struct LocalMusic {
     }
     }
   }
   }
 
 
-  // [Start itemMove_start]
-  // itemMoveGrid(index: number, newIndex: number): void {
-  //   if (!this.isDraggable(newIndex)) {
-  //     return;
-  //   }
-  //   let tmp = this.videoLocalList.splice(index, 1);
-  //   this.videoLocalList.splice(newIndex, 0, tmp[0]);
-  //   // this.bigItemIndex = this.videoLocalList.findIndex((item) => item === 0);
-  // }
-
   isInLeft(index: number) {
   isInLeft(index: number) {
     return index % 2 == 0;
     return index % 2 == 0;
   }
   }
@@ -4196,7 +4276,6 @@ export struct LocalMusic {
         .scale({ x: this.scaleItem === index ? 1.02 : 1, y: this.scaleItem === index ? 1.02 : 1 })
         .scale({ x: this.scaleItem === index ? 1.02 : 1, y: this.scaleItem === index ? 1.02 : 1 })
         .zIndex(this.dragItem === index ? 1 : 0)
         .zIndex(this.dragItem === index ? 1 : 0)
         .translate(this.dragItem === index ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 })
         .translate(this.dragItem === index ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 })
-        // .hitTestBehavior(this.isDraggable(this.videoLocalList.indexOf(item)) ? HitTestMode.Default : HitTestMode.None)
 
 
 
 
       }, (item: VideoItem) => item.filePath)
       }, (item: VideoItem) => item.filePath)
@@ -4217,7 +4296,6 @@ export struct LocalMusic {
     .scrollBar(BarState.Off)
     .scrollBar(BarState.Off)
     .supportAnimation(true)
     .supportAnimation(true)
     .cachedCount(this.twoFingerType==1?5:this.twoFingerType==2?4:3)
     .cachedCount(this.twoFingerType==1?5:this.twoFingerType==2?4:3)
-    // .columnsTemplate('1fr '.repeat(this.currentWidthBreakpoint === WidthBreakpoint.WIDTH_SM ? 2 : 5))
     .columnsTemplate(
     .columnsTemplate(
       this.twoFingerType == 3 ? 'repeat(auto-fit, 160)' :
       this.twoFingerType == 3 ? 'repeat(auto-fit, 160)' :
         this.twoFingerType == 2 ? 'repeat(auto-fit, 110)' : 'repeat(auto-fit, 80)'
         this.twoFingerType == 2 ? 'repeat(auto-fit, 110)' : 'repeat(auto-fit, 80)'
@@ -4236,16 +4314,11 @@ export struct LocalMusic {
 
 
       }))
       }))
     .enableScrollInteraction(true)
     .enableScrollInteraction(true)
-    // 滚轴滑动,记录下滑动时的起始位置和终点位置
     .onScrollIndex((start: number, end: number) => {
     .onScrollIndex((start: number, end: number) => {
       this.startIndex = start
       this.startIndex = start
       this.endIndex = end
       this.endIndex = end
     })
     })
     .onScrollFrameBegin((offset: number) => {
     .onScrollFrameBegin((offset: number) => {
-      //滚动小屏幕 比如puraX外屏和手机横屏可以隐藏bottomBarHeight topBarHeight
-      // if (this.isPhoneLan()) {
-      //   this.setBarHeightHide(offset)
-      // } else
       if (this.currentWidthBreakpoint !== WidthBreakpoint.WIDTH_SM ||
       if (this.currentWidthBreakpoint !== WidthBreakpoint.WIDTH_SM ||
         (this.currentHeightBreakpoint !== HeightBreakpoint.HEIGHT_MD &&
         (this.currentHeightBreakpoint !== HeightBreakpoint.HEIGHT_MD &&
           this.currentHeightBreakpoint !== HeightBreakpoint.HEIGHT_SM)) {
           this.currentHeightBreakpoint !== HeightBreakpoint.HEIGHT_SM)) {
@@ -4260,7 +4333,6 @@ export struct LocalMusic {
 
 
       return { offsetRemain: offset };
       return { offsetRemain: offset };
     })
     })
-    //允许拖拽音乐和视频到List或Grid上自动导入视频
     .allowDrop([uniformTypeDescriptor.UniformDataType.AUDIO,uniformTypeDescriptor.UniformDataType.VIDEO])
     .allowDrop([uniformTypeDescriptor.UniformDataType.AUDIO,uniformTypeDescriptor.UniformDataType.VIDEO])
     .onDrop((event?: DragEvent) => {
     .onDrop((event?: DragEvent) => {
       try {
       try {
@@ -4358,8 +4430,6 @@ export struct LocalMusic {
             })
             })
             .draggable(false)
             .draggable(false)
             .opacity(this.opacityItem)// 绑定透明度
             .opacity(this.opacityItem)// 绑定透明度
-            // .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
-            //   .animation({ duration: 500, curve: Curve.Ease }))
             .animation({
             .animation({
               duration: 666,
               duration: 666,
               curve: 'ease-in-out' // 可选动画曲线
               curve: 'ease-in-out' // 可选动画曲线
@@ -4537,8 +4607,6 @@ export struct LocalMusic {
     return wightG
     return wightG
   }
   }
   @State isShowDetail:boolean = false
   @State isShowDetail:boolean = false
-  // @State isShowDetailGrid:boolean = false
-  // @State isShowEditGrid:boolean = false
   @State longItemFilePathDetail:string =''
   @State longItemFilePathDetail:string =''
   @Builder
   @Builder
   MenuBuilder(item: VideoItem, index: number, filePath: string) {
   MenuBuilder(item: VideoItem, index: number, filePath: string) {
@@ -4578,6 +4646,16 @@ export struct LocalMusic {
                 this.longItemFilePath = ''
                 this.longItemFilePath = ''
               })
               })
 
 
+            MenuItem({
+              symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.music_note_list')),
+              content: '添加到歌单'
+            })
+              .visibility(item.type === CommonConstants.TYPE_IS_DIR ? Visibility.None : Visibility.Visible)
+              .onClick(async () => {
+                this.longItemFilePath = ''
+                await this.showAddToPlaylistDialog(item)
+              })
+
             MenuItem({
             MenuItem({
               symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.square_and_pencil')),
               symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.square_and_pencil')),
               content: '编辑标签'
               content: '编辑标签'
@@ -4593,7 +4671,6 @@ export struct LocalMusic {
               .onClick(async() => {
               .onClick(async() => {
                 this.setEditStrEmpty()
                 this.setEditStrEmpty()
                 this.tempLyricContent = await this.getLyricContent(item);
                 this.tempLyricContent = await this.getLyricContent(item);
-                // console.info('onecold 长按this.tempLyricContent' +this.tempLyricContent)
                 if(this.isGridMusic||this.twoFingerType==4){
                 if(this.isGridMusic||this.twoFingerType==4){
                   this.longItemFilePath = item.filePath
                   this.longItemFilePath = item.filePath
                 }else{
                 }else{
@@ -4678,7 +4755,6 @@ export struct LocalMusic {
     }
     }
 
 
   }
   }
-
   private listMaxScrollOffsetY: number = 0
   private listMaxScrollOffsetY: number = 0
   @State selectedIndex: number = -1
   @State selectedIndex: number = -1
 
 
@@ -4706,25 +4782,6 @@ export struct LocalMusic {
     this.pinchValue = 1;
     this.pinchValue = 1;
     this.scaleValue = 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
   @Builder
   getListView() {
   getListView() {
 
 
@@ -5078,6 +5135,7 @@ export struct LocalMusic {
   }
   }
 
 
   private async doPlay(item: VideoItem, index?: number, isFromSonPlayList?: boolean,isOpen?:boolean) {
   private async doPlay(item: VideoItem, index?: number, isFromSonPlayList?: boolean,isOpen?:boolean) {
+    Logger.info(`heanup doPlay被调用 - 歌曲: ${item.name}, 文件路径: ${item.filePath}, 索引: ${index}, 类型: ${item.type}, isFromSonPlayList: ${isFromSonPlayList}`)
 
 
     switch (item.type) {
     switch (item.type) {
       case CommonConstants.TYPE_IS_DIR:
       case CommonConstants.TYPE_IS_DIR:
@@ -5307,6 +5365,8 @@ export struct LocalMusic {
           if (index !== undefined) {
           if (index !== undefined) {
             this.curIndex = index
             this.curIndex = index
           }
           }
+          // 确保播放列表是完整的歌单歌曲列表(已在finishLoadingPlaylist中设置)
+          Logger.info(`heanup isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
         }else {
         }else {
           let globalVideoList = Utility.getGlobalList(this.videoLocalList, CommonConstants.TYPE_LOCAL) as VideoItem[];
           let globalVideoList = Utility.getGlobalList(this.videoLocalList, CommonConstants.TYPE_LOCAL) as VideoItem[];
           this.curIndex = Utility.getCurIndexFromGlobalList(globalVideoList, item.filePath)
           this.curIndex = Utility.getCurIndexFromGlobalList(globalVideoList, item.filePath)
@@ -5503,7 +5563,6 @@ export struct LocalMusic {
       this.showPlayerView()
       this.showPlayerView()
     })
     })
   }
   }
-
   //打开播放页
   //打开播放页
   showPlayerView() {
   showPlayerView() {
 
 
@@ -5518,8 +5577,6 @@ export struct LocalMusic {
       ToastUtil.showToast('当前播放列表为空,请先导入音乐。');
       ToastUtil.showToast('当前播放列表为空,请先导入音乐。');
     }
     }
   }
   }
-
-
   @Builder
   @Builder
   playConRigth() {
   playConRigth() {
     Row() {
     Row() {
@@ -5688,15 +5745,6 @@ export struct LocalMusic {
       return
       return
     }
     }
 
 
-    // let doChangeLyric = false
-    // if(item.filePath==this.currentSong?.filePath){
-    //   if(this.lyricConStr !== this.lyricContent){
-    //     doChangeLyric = true
-    //   }
-    // }else if(this.lyricConStr !== this.tempLyricContent){
-    //   doChangeLyric = true
-    // }
-
     //用户更改了封面,那就内嵌下封面
     //用户更改了封面,那就内嵌下封面
     if(StrUtil.isNotEmpty(this.imagePathStr)){
     if(StrUtil.isNotEmpty(this.imagePathStr)){
       const resultCover:boolean = await changeMusicCover(this.context,
       const resultCover:boolean = await changeMusicCover(this.context,
@@ -5712,8 +5760,6 @@ export struct LocalMusic {
       title: this.titleStr,
       title: this.titleStr,
       artist: this.artistStr,
       artist: this.artistStr,
       album: this.ablumStr,
       album: this.ablumStr,
-      // LYRICS:this.lyricConStr,
-      // USLT:this.lyricConStr,
       TYER:this.yearStr,
       TYER:this.yearStr,
       genre:this.genreStr,
       genre:this.genreStr,
       track:this.trackStr,
       track:this.trackStr,
@@ -5723,7 +5769,6 @@ export struct LocalMusic {
       lyricist:this.lyricistStr,
       lyricist:this.lyricistStr,
       TEXT:this.lyricistStr,//ID3v2 使用 TEXT 字段来表示作词者
       TEXT:this.lyricistStr,//ID3v2 使用 TEXT 字段来表示作词者
       comment:this.commentStr,
       comment:this.commentStr,
-      // comm:this.commentStr,//ID3v2:使用 COMM(Comment)字段。
       disc:this.discStr,
       disc:this.discStr,
     };
     };
 
 
@@ -6336,7 +6381,6 @@ export struct LocalMusic {
     }
     }
     .margin({ bottom: 20 })
     .margin({ bottom: 20 })
   }
   }
-
   setEditStrEmpty(){
   setEditStrEmpty(){
     this.imagePathStr = ''
     this.imagePathStr = ''
     this.lyricConStr = ''
     this.lyricConStr = ''
@@ -6363,7 +6407,6 @@ export struct LocalMusic {
     .width('100%')
     .width('100%')
     .height('100%')
     .height('100%')
   }
   }
-
   @Builder
   @Builder
   songDetail(currentItem:VideoItem) {
   songDetail(currentItem:VideoItem) {
     Column() {
     Column() {
@@ -7106,7 +7149,6 @@ export struct LocalMusic {
     this.sonDataSource.pushArrayData(this.songList)
     this.sonDataSource.pushArrayData(this.songList)
     this.curIndex = Utility.getIndexFromList(this.songList, this.videoUrl)
     this.curIndex = Utility.getIndexFromList(this.songList, this.videoUrl)
   }
   }
-
   @Builder
   @Builder
   PlayList() {
   PlayList() {
     List({ scroller: this.playListScroller }) {
     List({ scroller: this.playListScroller }) {
@@ -7824,7 +7866,6 @@ export struct LocalMusic {
   private castSeek: boolean = false;
   private castSeek: boolean = false;
   private castItem: avSession.AVQueueItem | undefined = undefined;
   private castItem: avSession.AVQueueItem | undefined = undefined;
   // @State isBgPlayOpen:boolean = true   //是否启用后台播放
   // @State isBgPlayOpen:boolean = true   //是否启用后台播放
-
   @State imageLabel: PixelMap | Resource = CommonConstants.musicBgList[0];
   @State imageLabel: PixelMap | Resource = CommonConstants.musicBgList[0];
   @State imageLabelBg: PixelMap | Resource = CommonConstants.musicBgList[0];
   @State imageLabelBg: PixelMap | Resource = CommonConstants.musicBgList[0];
   @State rotateAngle2: number = -9
   @State rotateAngle2: number = -9
@@ -7843,7 +7884,6 @@ export struct LocalMusic {
   @State lyricContent: string = ''
   @State lyricContent: string = ''
   @State isDebug: boolean = false
   @State isDebug: boolean = false
   @State isHightLightCenter: boolean = true
   @State isHightLightCenter: boolean = true
-
   /**
   /**
    * 初始化歌词加载与展示逻辑
    * 初始化歌词加载与展示逻辑
    * @param lyricPath 歌词文件路径(支持普通.lrc和加密.lrcc)
    * @param lyricPath 歌词文件路径(支持普通.lrc和加密.lrcc)
@@ -8568,7 +8608,6 @@ export struct LocalMusic {
     .position({ bottom: this.isHiCarSmall()?50:(this.isCoverOpacity() ? 55 : 70) }) // 将  固定在底部
     .position({ bottom: this.isHiCarSmall()?50:(this.isCoverOpacity() ? 55 : 70) }) // 将  固定在底部
 
 
   }
   }
-
   //播放控制上一首 下一首 暂停和播放
   //播放控制上一首 下一首 暂停和播放
   @Builder
   @Builder
   playCenterView(){
   playCenterView(){
@@ -9367,10 +9406,8 @@ export struct LocalMusic {
     }
     }
     .width('66%')
     .width('66%')
   }
   }
-
   @State isOpenPip: boolean = false
   @State isOpenPip: boolean = false
   @State pipBgIndex: number = 0
   @State pipBgIndex: number = 0
-
   // 构建字号控制器
   // 构建字号控制器
   @Builder
   @Builder
   BuildFontControls(isPip: boolean) {
   BuildFontControls(isPip: boolean) {
@@ -10162,7 +10199,6 @@ export struct LocalMusic {
       ? '歌词时间已重置'
       ? '歌词时间已重置'
       : `歌词已${this.timeOffset < 0 ? '延后' : '提前'} ${absValue.toFixed(1)} 秒`
       : `歌词已${this.timeOffset < 0 ? '延后' : '提前'} ${absValue.toFixed(1)} 秒`
   }
   }
-
   // 导入  本地歌词 搜索歌词
   // 导入  本地歌词 搜索歌词
   @Builder
   @Builder
   pushLyricButton(icon: Resource, step: number, label: string) {
   pushLyricButton(icon: Resource, step: number, label: string) {
@@ -10770,8 +10806,8 @@ export struct LocalMusic {
         this.isShowMoreView = false
         this.isShowMoreView = false
         break;
         break;
       case 20://AB循环
       case 20://AB循环
-        this.isABSheet = !this.isABSheet;
-        this.isShowMoreView = false
+        this.isOpenAB = !this.isOpenAB;
+        this.isABSheet = false
         break;
         break;
     }
     }
 
 
@@ -10798,8 +10834,8 @@ export struct LocalMusic {
       onClickBItem: async ()=>{
       onClickBItem: async ()=>{
         if(this.mIjkMediaPlayer){
         if(this.mIjkMediaPlayer){
           this.jumpBTime = await this.mIjkMediaPlayer.getCurrentPosition()
           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
             this.jumpBTime = 0
           }
           }
         }
         }
@@ -10939,7 +10975,6 @@ export struct LocalMusic {
     }
     }
     Logger.info(`[${TAG}] onecold onStateChange: ${this.curState}, reason: ${reason}`);
     Logger.info(`[${TAG}] onecold onStateChange: ${this.curState}, reason: ${reason}`);
   }
   }
-
   onActionEvent(event: PiPWindow.PiPActionEventType, status: number | undefined) {
   onActionEvent(event: PiPWindow.PiPActionEventType, status: number | undefined) {
     LogUtil.info('onecold onActionEvent = ' + event + '  status=' + status)
     LogUtil.info('onecold onActionEvent = ' + event + '  status=' + status)
     switch (event) {
     switch (event) {
@@ -10970,7 +11005,6 @@ export struct LocalMusic {
     this.buttonAction = event + `-status:${status}`;
     this.buttonAction = event + `-status:${status}`;
     Logger.info(`[${TAG}] onActionEvent: ${this.buttonAction} status:${status}}`);
     Logger.info(`[${TAG}] onActionEvent: ${this.buttonAction} status:${status}}`);
   }
   }
-
   /**
   /**
    * 画中画功能(悬浮歌词功能)结束
    * 画中画功能(悬浮歌词功能)结束
    *
    *
@@ -11472,7 +11506,6 @@ export struct LocalMusic {
     this.loadingVisible = Visibility.None;
     this.loadingVisible = Visibility.None;
     this.replayVisible = Visibility.Visible;
     this.replayVisible = Visibility.Visible;
   }
   }
-
   private async play(url: string,startOffset?:number) {
   private async play(url: string,startOffset?:number) {
     let that = this;
     let that = this;
     that.showLoadIng();
     that.showLoadIng();
@@ -12040,6 +12073,23 @@ export struct LocalMusic {
 
 
   public setIsPlaying(isPlayer: boolean) {
   public setIsPlaying(isPlayer: boolean) {
     this.isPlaying = isPlayer;
     this.isPlaying = isPlayer;
+
+    // 发送播放状态变化事件给歌单详情页面
+    try {
+      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,
+          currentFilePath: currentFilePath
+        }
+      };
+      emitter.emit(eventPlaybackStatus, eventData)
+      Logger.info(`heanup 发送播放状态变化事件: isPlaying=${this.isPlaying}, curIndex=${this.curIndex}, currentFilePath=${currentFilePath}`)
+    } catch (error) {
+      Logger.error('heanup 发送播放状态变化事件失败: ' + error)
+    }
   }
   }
 
 
   private setPlaybackStateChangeListener(): void {
   private setPlaybackStateChangeListener(): void {
@@ -12245,7 +12295,6 @@ export struct LocalMusic {
 
 
 
 
   };
   };
-
   /**
   /**
    * Gesture method onActionUpdate.
    * Gesture method onActionUpdate.
    *
    *
@@ -12275,7 +12324,6 @@ export struct LocalMusic {
     this.currentTime = this.stringForTime(position);
     this.currentTime = this.stringForTime(position);
     this.isCurrentTime = false
     this.isCurrentTime = false
   }
   }
-
   private sessionRewindCallback = (time?: number) => {
   private sessionRewindCallback = (time?: number) => {
     if (!time) {
     if (!time) {
       return;
       return;
@@ -13019,9 +13067,114 @@ export struct LocalMusic {
    * 穿山甲广告代码结束
    * 穿山甲广告代码结束
    */
    */
 
 
+  /**
+   * 处理歌单播放请求
+   */
+  private handlePlaylistPlayRequest(playlistId: string, playlistName: string, songFilePaths: string[], startIndex: number) {
+    try {
+      Logger.info(`heanup 处理歌单播放请求: ${playlistName}, 歌曲数量: ${songFilePaths.length}, 开始索引: ${startIndex}`)
+
+      // 从数据库重新加载这些歌曲,使用索引记录位置以保持顺序
+      const songMap: Map<number, VideoItem> = new Map()
+      let completedQueries = 0
 
 
-}
+      Logger.info(`heanup 开始从数据库查询 ${songFilePaths.length} 首歌曲`)
+
+      // 遍历所有文件路径,使用索引记录位置
+      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,开始等待结果[${index}]: ${filePath}`)
+
+        queryPromise.then((videoItem) => {
+          completedQueries++
+          Logger.info(`heanup 查询Promise返回结果[${index}]: ${filePath}, 找到歌曲: ${videoItem ? videoItem.name : 'null'}`)
+
+          if (videoItem) {
+            // 使用索引作为key保存到Map中,保持原始顺序
+            songMap.set(index, videoItem)
+            Logger.info(`heanup 从数据库找到歌曲[${index}]: ${videoItem.name} (${completedQueries}/${songFilePaths.length})`)
+          } else {
+            Logger.warn(`heanup 数据库中未找到歌曲[${index}]: ${filePath} (${completedQueries}/${songFilePaths.length})`)
+          }
 
 
+          // 检查是否所有歌曲都已加载完成
+          if (completedQueries === songFilePaths.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 查询歌曲失败[${index}]: ${filePath}, 错误: ${error.message} (${completedQueries}/${songFilePaths.length})`)
+
+          // 即使出错也要检查是否完成所有查询
+          if (completedQueries === songFilePaths.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)
+          }
+        })
+      }
+    } catch (error) {
+      Logger.error('heanup 处理简化歌单播放请求失败: ' + error)
+      ToastUtil.showToast('播放失败')
+    }
+  }
+
+  /**
+   * 完成歌单加载并开始播放
+   */
+  private finishLoadingPlaylist(songs: VideoItem[], startIndex: number, playlistName: string) {
+    if (songs.length === 0) {
+      Logger.error('heanup 没有找到任何可播放的歌曲')
+      ToastUtil.showToast('没有找到可播放的歌曲')
+      return
+    }
+    this.songList = songs
+
+    this.sonDataSource.pushArrayData(songs)
+
+    this.sonDataSource.notifyDataReload()
+
+    this.curIndex = startIndex
+
+    if (songs[startIndex]) {
+      // 确保当前播放的歌曲也更新到存储
+      AppStorage.setOrCreate('currentSong', songs[startIndex])
+      // 设置isFromSonPlayList=true,避免doPlay方法重新从全局列表构建播放列表
+      this.doPlay(songs[startIndex], startIndex, true)
+    }
+
+    PreferencesUtil.putSync('LastMusicList', this.songList)
+
+    ToastUtil.showToast(`开始播放歌单: ${playlistName}`)
+  }
+}
 //视频气泡窗口的布局
 //视频气泡窗口的布局
 @Builder
 @Builder
 function customPopupBuilder(dataBu: BubbleBean) {
 function customPopupBuilder(dataBu: BubbleBean) {
@@ -13172,51 +13325,4 @@ function getFileDirName(filePath: string,rootPath:string): string{
   if(result.startsWith('.'))
   if(result.startsWith('.'))
     result = result.replace(/\./g, '')
     result = result.replace(/\./g, '')
   return result
   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);
-  }
-}
-
-
-
+}

+ 0 - 1
entry/src/main/ets/view/TitleBar.ets

@@ -509,7 +509,6 @@ export namespace TitleBar {
 
 
     setTitleBarBackground(value: ResourceColor): Model {
     setTitleBarBackground(value: ResourceColor): Model {
       this.titleBarBackground = value;
       this.titleBarBackground = value;
-      console.log('Heanup: TitleBar背景色:'+JSON.stringify(value))
       return this;
       return this;
     }
     }
 
 

+ 51 - 0
entry/src/main/ets/viewmodel/Playlist.ets

@@ -0,0 +1,51 @@
+import { VideoItem } from './VideoItem';
+
+/**
+ * 歌单数据模型
+ */
+@Observed
+export class Playlist {
+  id: string
+  name: string
+  coverPath?: string
+  description?: string
+  createTime: string
+  updateTime: string
+  songCount: number
+  sortOrder: number
+  songs?: VideoItem[] // 用于UI展示的歌曲列表
+
+  constructor(id: string, name: string, createTime: string, updateTime: string, 
+              songCount: number = 0, sortOrder: number = 0, 
+              coverPath?: string, description?: string, songs?: VideoItem[]) {
+    this.id = id
+    this.name = name
+    this.coverPath = coverPath
+    this.description = description
+    this.createTime = createTime
+    this.updateTime = updateTime
+    this.songCount = songCount
+    this.sortOrder = sortOrder
+    this.songs = songs
+  }
+}
+
+/**
+ * 歌单歌曲关联模型
+ */
+export class PlaylistSong {
+  id: string
+  playlistId: string
+  songFilePath: string
+  addTime: string
+  sortOrder: number
+
+  constructor(id: string, playlistId: string, songFilePath: string, 
+              addTime: string, sortOrder: number = 0) {
+    this.id = id
+    this.playlistId = playlistId
+    this.songFilePath = songFilePath
+    this.addTime = addTime
+    this.sortOrder = sortOrder
+  }
+}

+ 28 - 0
entry/src/main/resources/base/element/color.json

@@ -191,6 +191,34 @@
     {
     {
       "name": "main_color",
       "name": "main_color",
       "value": "#103fb6"
       "value": "#103fb6"
+    },
+    {
+      "name": "theme_color",
+      "value": "#007DFF"
+    },
+    {
+      "name": "sheet_background",
+      "value": "#FFFFFF"
+    },
+    {
+      "name": "secondary_button_background",
+      "value": "#F5F5F5"
+    },
+    {
+      "name": "dialog_background",
+      "value": "#FFFFFF"
+    },
+    {
+      "name": "input_background",
+      "value": "#F5F5F5"
+    },
+    {
+      "name": "cancel_button_background",
+      "value": "#F5F5F5"
+    },
+    {
+      "name": "cancel_button_text",
+      "value": "#666666"
     }
     }
   ]
   ]
 }
 }

+ 144 - 0
entry/src/main/resources/base/element/string.json

@@ -482,6 +482,150 @@
     {
     {
       "name": "back",
       "name": "back",
       "value": "返回"
       "value": "返回"
+    },
+    {
+      "name": "playlist",
+      "value": "歌单"
+    },
+    {
+      "name": "create_playlist",
+      "value": "创建歌单"
+    },
+    {
+      "name": "edit_playlist",
+      "value": "编辑歌单"
+    },
+    {
+      "name": "delete_playlist",
+      "value": "删除歌单"
+    },
+    {
+      "name": "playlist_name",
+      "value": "歌单名称"
+    },
+    {
+      "name": "playlist_description",
+      "value": "歌单描述"
+    },
+    {
+      "name": "playlist_name_placeholder",
+      "value": "请输入歌单名称"
+    },
+    {
+      "name": "playlist_description_placeholder",
+      "value": "请输入歌单描述"
+    },
+    {
+      "name": "add_to_playlist",
+      "value": "添加到歌单"
+    },
+    {
+      "name": "batch_add_to_playlist",
+      "value": "批量添加到歌单"
+    },
+    {
+      "name": "select_playlist",
+      "value": "选择歌单"
+    },
+    {
+      "name": "play_all",
+      "value": "播放全部"
+    },
+    {
+      "name": "playlist_empty",
+      "value": "歌单为空"
+    },
+    {
+      "name": "playlist_empty_tip",
+      "value": "添加一些歌曲到歌单中"
+    },
+    {
+      "name": "no_playlist",
+      "value": "暂无歌单"
+    },
+    {
+      "name": "no_playlist_tip",
+      "value": "创建歌单来管理你的音乐"
+    },
+    {
+      "name": "new_playlist",
+      "value": "新建歌单"
+    },
+    {
+      "name": "songs_count",
+      "value": "首歌曲"
+    },
+    {
+      "name": "remove_from_playlist",
+      "value": "从歌单移除"
+    },
+    {
+      "name": "playlist_operations",
+      "value": "歌单操作"
+    },
+    {
+      "name": "playlist_created_success",
+      "value": "歌单创建成功"
+    },
+    {
+      "name": "playlist_updated_success",
+      "value": "歌单更新成功"
+    },
+    {
+      "name": "playlist_deleted_success",
+      "value": "歌单删除成功"
+    },
+    {
+      "name": "playlist_not_exist",
+      "value": "歌单不存在"
+    },
+    {
+      "name": "confirm_delete_playlist",
+      "value": "确定要删除歌单"
+    },
+    {
+      "name": "confirm_delete_playlist_tip",
+      "value": "吗?此操作不可撤销。"
+    },
+    {
+      "name": "playlist_name_required",
+      "value": "请输入歌单名称"
+    },
+    {
+      "name": "playlist_name_too_long",
+      "value": "歌单名称不能超过50个字符"
+    },
+    {
+      "name": "playlist_description_too_long",
+      "value": "歌单描述不能超过200个字符"
+    },
+    {
+      "name": "song_added_to_playlist",
+      "value": "已添加到"
+    },
+    {
+      "name": "song_removed_from_playlist",
+      "value": "已从"
+    },
+    {
+      "name": "song_removed_from_playlist_suffix",
+      "value": "移除"
+    },
+    {
+      "name": "batch_add_success",
+      "value": "已添加"
+    },
+    {
+      "name": "batch_add_success_suffix",
+      "value": "首歌曲到"
+    },
+    {
+      "name": "songs_already_in_playlist",
+      "value": "首歌曲已在歌单中"
+    },
+    {
+      "name": "playlist_play_started",
+      "value": "开始播放歌单"
     }
     }
   ]
   ]
 }
 }

+ 2 - 1
entry/src/main/resources/base/profile/main_pages.json

@@ -6,6 +6,7 @@
     "pages/NewIndex",
     "pages/NewIndex",
     "pages/VerifyPage",
     "pages/VerifyPage",
     "pages/VipPage",
     "pages/VipPage",
-    "pages/Demo"
+    "pages/Demo",
+    "pages/PlaylistDetailPage"
   ]
   ]
 }
 }