Просмотр исходного кода

feat(MediaTable): 添加分页查询功能支持

- 新增 PageQueryOptions 和 PageQueryResult 接口定义
- 实现首页文件夹内容分页查询功能
- 实现媒体库分页查询功能
- 实现艺术家维度分页查询功能
- 实现专辑维度分页查询功能
- 添加搜索条件应用方法
- 添加排序条件应用方法
- 添加总记录数获取方法
- 在 LocalMusic 中集成分页查询功能
- 实现分页状态管理和缓存机制
- 添加触底加载下一页功能
- 优化播放列表加载逻辑
chendeben 7 месяцев назад
Родитель
Сommit
c786bf5ecb
2 измененных файлов с 731 добавлено и 185 удалено
  1. 352 0
      entry/src/main/ets/common/util/MediaTable.ets
  2. 379 185
      entry/src/main/ets/view/LocalMusic.ets

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

@@ -80,6 +80,24 @@ function normalizeFilePath(filePath: string): string {
   return filePath;
 }
 
+/**
+ * 分页查询接口 - 支持条件查询、排序、分页
+ */
+export interface PageQueryOptions {
+  parentPath?: string;        // 文件夹路径(首页模式)
+  searchKeyword?: string;      // 搜索关键词
+  sortType?: number;           // 排序类型
+  pageIndex: number;           // 页码(从0开始)
+  pageSize: number;            // 每页数量
+  type?: number;               // 媒体类型过滤
+}
+
+export interface PageQueryResult {
+  items: VideoItem[];
+  totalCount: number;
+  hasMore: boolean;
+}
+
 export default  class MediaTable {
   private accountTable = new RdbUtils(RdbUtils.MEDIA_TABLE.tableName, RdbUtils.MEDIA_TABLE.sqlCreate,
     RdbUtils.MEDIA_TABLE.columns);
@@ -1028,6 +1046,340 @@ export default  class MediaTable {
     });
   }
 
+  /**
+   * 应用搜索条件到查询谓词
+   * 支持对 name、artist、album、fileName 多字段模糊搜索
+   */
+  private applySearchConditions(predicates: relationalStore.RdbPredicates, searchKeyword: string): void {
+    if (searchKeyword && searchKeyword.trim().length > 0) {
+      const keyword = `%${searchKeyword.trim()}%`;
+      predicates.beginWrap();
+      predicates.like(DB_COLUMNS.NAME, keyword);
+      predicates.or();
+      predicates.like(DB_COLUMNS.ARTIST, keyword);
+      predicates.or();
+      predicates.like(DB_COLUMNS.ALBUM, keyword);
+      predicates.or();
+      predicates.like(DB_COLUMNS.FILE_NAME, keyword);
+      predicates.endWrap();
+    }
+  }
+
+  /**
+   * 应用排序条件到查询谓词
+   * sortType: 0-文件名升序 1-文件名降序 2-艺术家升序 3-艺术家降序 4-专辑升序 5-专辑降序
+   */
+  private applySortConditions(predicates: relationalStore.RdbPredicates, sortType?: number): void {
+    const type = sortType !== undefined ? sortType : 4;
+    switch (type) {
+      case 0:
+        predicates.orderByAsc(DB_COLUMNS.NAME);
+        break;
+      case 1:
+        predicates.orderByDesc(DB_COLUMNS.NAME);
+        break;
+      case 2:
+        predicates.orderByAsc(DB_COLUMNS.ARTIST);
+        break;
+      case 3:
+        predicates.orderByDesc(DB_COLUMNS.ARTIST);
+        break;
+      case 4:
+        predicates.orderByAsc(DB_COLUMNS.ALBUM);
+        break;
+      case 5:
+        predicates.orderByDesc(DB_COLUMNS.ALBUM);
+        break;
+      default:
+        predicates.orderByAsc(DB_COLUMNS.NAME);
+        break;
+    }
+  }
+
+  /**
+   * 获取总记录数(用于计算分页)
+   */
+  private async getTotalCount(predicates: relationalStore.RdbPredicates): Promise<number> {
+    return new Promise((resolve, reject) => {
+      this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
+        try {
+          const count = resultSet.rowCount;
+          resolve(count);
+        } catch (err) {
+          const error = err as Error;
+          Logger.error(RdbUtils.RDB_TAG, `getTotalCount error: ${error.message}`);
+          reject(err);
+        } finally {
+          resultSet.close();
+        }
+      });
+    });
+  }
+
+  /**
+   * 首页文件夹内容分页查询(modeType=0)
+   * 注意:此方法只返回数据库中的文件记录,不包含文件夹
+   * 文件夹需要通过文件系统API单独获取
+   */
+  public async queryByParentPathPaged(options: PageQueryOptions): Promise<PageQueryResult> {
+    try {
+      Logger.info('heanup MediaTable', `queryByParentPathPaged: 开始分页查询 parentPath=${options.parentPath}, page=${options.pageIndex}, size=${options.pageSize}`);
+      
+      // 1. 构建基础查询条件
+      const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+      if (options.parentPath) {
+        predicates.equalTo(DB_COLUMNS.PARENT_PATH, options.parentPath);
+      }
+      
+      // 2. 应用搜索条件
+      if (options.searchKeyword) {
+        this.applySearchConditions(predicates, options.searchKeyword);
+      }
+      
+      // 3. 先获取总数
+      const totalCount = await this.getTotalCount(predicates);
+      
+      // 4. 构建分页查询条件
+      const pagePredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+      if (options.parentPath) {
+        pagePredicates.equalTo(DB_COLUMNS.PARENT_PATH, options.parentPath);
+      }
+      if (options.searchKeyword) {
+        this.applySearchConditions(pagePredicates, options.searchKeyword);
+      }
+      
+      // 5. 应用排序
+      this.applySortConditions(pagePredicates, options.sortType);
+      
+      // 6. 应用分页
+      const offset = options.pageIndex * options.pageSize;
+      pagePredicates.limitAs(options.pageSize);
+      pagePredicates.offsetAs(offset);
+      
+      // 7. 执行查询
+      return new Promise((resolve, reject) => {
+        this.accountTable.query(pagePredicates, (resultSet: relationalStore.ResultSet) => {
+          try {
+            const items = this.parseResultSetToVideoItems(resultSet);
+            const hasMore = (offset + items.length) < totalCount;
+            Logger.info('heanup MediaTable', `queryByParentPathPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
+            resolve({ items, totalCount, hasMore });
+          } catch (err) {
+            const error = err as Error;
+            Logger.error('heanup MediaTable', `queryByParentPathPaged error: ${error.message}`);
+            reject(err);
+          }
+        });
+      });
+    } catch (err) {
+      const error = err as Error;
+      Logger.error('heanup MediaTable', `queryByParentPathPaged failed: ${error.message}`);
+      throw error;
+    }
+  }
+
+  /**
+   * 媒体库分页查询(modeType=1)
+   */
+  public async queryMediaLibraryPaged(options: PageQueryOptions): Promise<PageQueryResult> {
+    try {
+      Logger.info('heanup MediaTable', `queryMediaLibraryPaged: 开始分页查询 page=${options.pageIndex}, size=${options.pageSize}`);
+      
+      // 1. 构建基础查询条件 - 查询所有type=0的音乐文件
+      const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+      predicates.equalTo(DB_COLUMNS.TYPE, 0);
+      
+      // 2. 应用搜索条件
+      if (options.searchKeyword) {
+        this.applySearchConditions(predicates, options.searchKeyword);
+      }
+      
+      // 3. 先获取总数
+      const totalCount = await this.getTotalCount(predicates);
+      
+      // 4. 构建分页查询条件
+      const pagePredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+      pagePredicates.equalTo(DB_COLUMNS.TYPE, 0);
+      if (options.searchKeyword) {
+        this.applySearchConditions(pagePredicates, options.searchKeyword);
+      }
+      
+      // 5. 应用排序
+      this.applySortConditions(pagePredicates, options.sortType);
+      
+      // 6. 应用分页
+      const offset = options.pageIndex * options.pageSize;
+      pagePredicates.limitAs(options.pageSize);
+      pagePredicates.offsetAs(offset);
+      
+      // 7. 执行查询
+      return new Promise((resolve, reject) => {
+        this.accountTable.query(pagePredicates, (resultSet: relationalStore.ResultSet) => {
+          try {
+            const items = this.parseResultSetToVideoItems(resultSet);
+            const hasMore = (offset + items.length) < totalCount;
+            Logger.info('heanup MediaTable', `queryMediaLibraryPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
+            resolve({ items, totalCount, hasMore });
+          } catch (err) {
+            const error = err as Error;
+            Logger.error('heanup MediaTable', `queryMediaLibraryPaged error: ${error.message}`);
+            reject(err);
+          }
+        });
+      });
+    } catch (err) {
+      const error = err as Error;
+      Logger.error('heanup MediaTable', `queryMediaLibraryPaged failed: ${error.message}`);
+      throw error;
+    }
+  }
+
+  /**
+   * 艺术家维度分页查询(modeType=2)
+   * 返回艺术家封面列表的代表性歌曲
+   */
+  public async queryArtistsPaged(options: PageQueryOptions): Promise<PageQueryResult> {
+    try {
+      Logger.info('heanup MediaTable', `queryArtistsPaged: 开始分页查询 page=${options.pageIndex}, size=${options.pageSize}`);
+      
+      // 艺术家模式:返回每个艺术家的第一首歌作为代表
+      // 先查询所有不同的艺术家
+      const artistPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+      artistPredicates.isNotNull(DB_COLUMNS.ARTIST);
+      artistPredicates.notEqualTo(DB_COLUMNS.ARTIST, '');
+      artistPredicates.distinct();
+      
+      return new Promise((resolve, reject) => {
+        this.accountTable.query(artistPredicates, async (resultSet: relationalStore.ResultSet) => {
+          try {
+            const artists: string[] = this.parseDistinctColumn(resultSet, DB_COLUMNS.ARTIST);
+            
+            // 应用搜索过滤
+            let filteredArtists = artists;
+            if (options.searchKeyword) {
+              const keyword = options.searchKeyword.toLowerCase();
+              filteredArtists = artists.filter(artist => 
+                artist.toLowerCase().includes(keyword)
+              );
+            }
+            
+            // 排序
+            filteredArtists.sort((a, b) => a.localeCompare(b));
+            
+            const totalCount = filteredArtists.length;
+            const offset = options.pageIndex * options.pageSize;
+            const pagedArtists = filteredArtists.slice(offset, offset + options.pageSize);
+            
+            // 为每个艺术家查询第一首歌
+            const items: VideoItem[] = [];
+            for (const artist of pagedArtists) {
+              const songPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+              songPredicates.equalTo(DB_COLUMNS.ARTIST, artist);
+              songPredicates.limitAs(1);
+              
+              await new Promise<void>((resolveInner) => {
+                this.accountTable.query(songPredicates, (songResultSet: relationalStore.ResultSet) => {
+                  if (songResultSet.rowCount > 0) {
+                    songResultSet.goToFirstRow();
+                    const item = this.buildVideoItem(songResultSet);
+                    items.push(item);
+                  }
+                  songResultSet.close();
+                  resolveInner();
+                });
+              });
+            }
+            
+            const hasMore = (offset + items.length) < totalCount;
+            Logger.info('heanup MediaTable', `queryArtistsPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
+            resolve({ items, totalCount, hasMore });
+          } catch (err) {
+            const error = err as Error;
+            Logger.error('heanup MediaTable', `queryArtistsPaged error: ${error.message}`);
+            reject(err);
+          }
+        });
+      });
+    } catch (err) {
+      const error = err as Error;
+      Logger.error('heanup MediaTable', `queryArtistsPaged failed: ${error.message}`);
+      throw error;
+    }
+  }
+
+  /**
+   * 专辑维度分页查询(modeType=3)
+   * 返回专辑封面列表的代表性歌曲
+   */
+  public async queryAlbumsPaged(options: PageQueryOptions): Promise<PageQueryResult> {
+    try {
+      Logger.info('heanup MediaTable', `queryAlbumsPaged: 开始分页查询 page=${options.pageIndex}, size=${options.pageSize}`);
+      
+      // 专辑模式:返回每个专辑的第一首歌作为代表
+      // 先查询所有不同的专辑
+      const albumPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+      albumPredicates.isNotNull(DB_COLUMNS.ALBUM);
+      albumPredicates.notEqualTo(DB_COLUMNS.ALBUM, '');
+      albumPredicates.distinct();
+      
+      return new Promise((resolve, reject) => {
+        this.accountTable.query(albumPredicates, async (resultSet: relationalStore.ResultSet) => {
+          try {
+            const albums: string[] = this.parseDistinctColumn(resultSet, DB_COLUMNS.ALBUM);
+            
+            // 应用搜索过滤
+            let filteredAlbums = albums;
+            if (options.searchKeyword) {
+              const keyword = options.searchKeyword.toLowerCase();
+              filteredAlbums = albums.filter(album => 
+                album.toLowerCase().includes(keyword)
+              );
+            }
+            
+            // 排序
+            filteredAlbums.sort((a, b) => a.localeCompare(b));
+            
+            const totalCount = filteredAlbums.length;
+            const offset = options.pageIndex * options.pageSize;
+            const pagedAlbums = filteredAlbums.slice(offset, offset + options.pageSize);
+            
+            // 为每个专辑查询第一首歌
+            const items: VideoItem[] = [];
+            for (const album of pagedAlbums) {
+              const songPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
+              songPredicates.equalTo(DB_COLUMNS.ALBUM, album);
+              songPredicates.limitAs(1);
+              
+              await new Promise<void>((resolveInner) => {
+                this.accountTable.query(songPredicates, (songResultSet: relationalStore.ResultSet) => {
+                  if (songResultSet.rowCount > 0) {
+                    songResultSet.goToFirstRow();
+                    const item = this.buildVideoItem(songResultSet);
+                    items.push(item);
+                  }
+                  songResultSet.close();
+                  resolveInner();
+                });
+              });
+            }
+            
+            const hasMore = (offset + items.length) < totalCount;
+            Logger.info('heanup MediaTable', `queryAlbumsPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
+            resolve({ items, totalCount, hasMore });
+          } catch (err) {
+            const error = err as Error;
+            Logger.error('heanup MediaTable', `queryAlbumsPaged error: ${error.message}`);
+            reject(err);
+          }
+        });
+      });
+    } catch (err) {
+      const error = err as Error;
+      Logger.error('heanup MediaTable', `queryAlbumsPaged failed: ${error.message}`);
+      throw error;
+    }
+  }
+
 }
 
 function generateBucket(item: VideoItem): relationalStore.ValuesBucket {

+ 379 - 185
entry/src/main/ets/view/LocalMusic.ets

@@ -46,7 +46,7 @@ import { PointLightButton } from './PointLight/PointLightButton';
 import { PlayConstants } from '../common/constants/PlayConstants';
 import { effectKit } from '@kit.ArkGraphics2D';
 import { ColorConversion } from '../common/util/ColorConversion';
-import { LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
+import { Lyric, LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
 import { AvSessionController } from '../controller/AvSessionController';
 import { repairAudioMetadata, convertDsfToWav, getApiLyric,changeMusicCover} from '../common/util/MusicTagUtils';
 import { extractHwMediaMetadata, FFMpegTags,Utility } from '../common/util/Utility';
@@ -76,7 +76,7 @@ import { secondToTime,getTransverterText } from '../common/util/CommUtils';
 import { CommonConstants2 } from '../common/util/CommonConstants2';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { LazyDataSource } from '../common/util/LazyDataSource';
-import  MediaTable  from '../common/util/MediaTable';
+import  MediaTable, { PageQueryOptions, PageQueryResult }  from '../common/util/MediaTable';
 import FileDeletionWatcher from '../common/util/FileDeletionWatcher';
 import LyricUtil from '../common/util/LyricUtil';
 import NetAxiosUtil from '../common/util/NetAxiosUtil';
@@ -397,6 +397,16 @@ export struct LocalMusic {
   private playListScroller: Scroller = new Scroller()
   @State isOneKeyTags:boolean = false
   @State isFixMessy:boolean = false
+  
+  // 分页相关状态
+  @State private currentPage: number = 0          // 当前页码
+  @State private pageSize: number = 50            // 每页数量
+  @State private totalCount: number = 0           // 总记录数
+  @State private hasMoreData: boolean = true      // 是否还有更多数据
+  @State private isLoadingPage: boolean = false   // 是否正在加载分页数据
+  private pageCache: Map<number, VideoItem[]> = new Map()  // 页面缓存(最多保留3页)
+  private fullSongList: VideoItem[] = []          // 完整的播放列表(延迟加载,只在播放时加载)
+  private isFullSongListLoaded: boolean = false   // 标记完整列表是否已加载
   @StorageProp('isLandscape') @Watch('onIsLandscapeChange')  isLandscape: boolean = false;
   @StorageProp('topSafeHeight') topSafeHeight: number = 0;
   @StorageProp('bottomSafeHeight') bottomSafeHeight: number = 0;
@@ -476,21 +486,25 @@ export struct LocalMusic {
     this.isRefreshing = false
   }
 
-  onModeChange() {
+  async onModeChange(): Promise<void> {
     this.isFavMusic = false
     this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
+    
+    Logger.info('heanup LocalMusic', `onModeChange: 切换模式 modeType=${this.modeType}`);
+    
+    // 模式切换时重置分页状态并加载第一页
+    await this.resetAndLoadFirstPage();
+    
     switch (this.modeType) {
       case 0:
-        this.getSortedFiles(this.currentPath)
+        // 首页模式已在 resetAndLoadFirstPage 中处理
         break
       case 1:
         this.rightTopImage = $r('sys.symbol.sort')
-        this.updateListData(this.mediaKuList)
         this.titleName = Utility.resourceToString(this.context,$r('app.string.media_ku'))
         break
       case 2:
         this.rightTopImage = $r('sys.symbol.sort')
-        this.updateListData(this.artistList)
         this.titleName = Utility.resourceToString(this.context,$r('app.string.artist'))
         break
 
@@ -871,7 +885,7 @@ export struct LocalMusic {
     AppStorage.setOrCreate('themeColor', themeColor);
     this.themeColor = themeColor;
     this.doChangeSetting()
-    this.windowClass.on('windowSizeChange', (size) => {
+    this.windowClass.on('windowSizeChange', (size: window.Size) => {
       LogUtil.info('onecold  windowSizeChange')
       this.doChangeBarHeight()
       let viewWidth = px2vp(size.width);
@@ -1138,8 +1152,9 @@ export struct LocalMusic {
           Logger.info('onecold 查询媒体库列表 this.mediaKuList length= ' + this.mediaKuList.length)
           Utility.doSortListAscending(this.mediaKuList)
           AppStorage.setOrCreate('mediaKuList', this.mediaKuList);
+          // 媒体库模式下重新加载第一页(分页查询)
           if (this.modeType === 1) {
-            this.updateListData(this.mediaKuList)
+            this.resetAndLoadFirstPage();
           }
           PreferencesUtil.putSync('mediaKuCount', this.mediaKuList.length)
           // 处理媒体库前100条记录,列表长度并存储到PreferencesUtil
@@ -1157,6 +1172,11 @@ export struct LocalMusic {
           this.artistList = e.data.data2;
           PreferencesUtil.putSync('artistCount', this.artistList.length)
 
+          // 艺术家模式下重新加载第一页(分页查询)
+          if (this.modeType === 2) {
+            this.resetAndLoadFirstPage();
+          }
+
           // 处理艺术家前100条记录,列表长度并存储到PreferencesUtil
           if (ArrayUtil.isNotEmpty(this.artistList)&&this.artistList.length  > 100) {
             // 只保存前100条记录到缓存中
@@ -1178,6 +1198,12 @@ export struct LocalMusic {
           this.albumMap = e.data.data1;
 
           this.albumList = e.data.data2
+          
+          // 专辑模式下重新加载第一页(分页查询)
+          if (this.modeType === 3) {
+            this.resetAndLoadFirstPage();
+          }
+          
           //查询完毕在启动是否自动播放
           if (this.isStartAutoPlay&&this.isCanAuto) {
             this.isCanAuto = false
@@ -1292,7 +1318,7 @@ export struct LocalMusic {
       if (payload.lyricContent) {
         this.applyInlineLyricContent(payload.lyricContent);
       }
-      let avSessionDurationMs = this.duration;
+      let avSessionDurationMs: number = this.duration;
       if (payload.duration) {
         const durationSeconds = Number(payload.duration);
         if (!Number.isNaN(durationSeconds) && durationSeconds > 0) {
@@ -1342,7 +1368,7 @@ export struct LocalMusic {
       this.name = this.titleStr;
 
       // 同步当前播放的歌曲信息
-      if (this.editingItem && this.editingItem.filePath == this.currentSong?.filePath) {
+      if (this.editingItem && this.currentSong && this.editingItem.filePath == this.currentSong.filePath) {
         this.currentSong.name = this.titleStr;
         this.artist = this.artistStr;
         this.currentSong.artist = this.artistStr;
@@ -1514,8 +1540,8 @@ export struct LocalMusic {
       return;
     }
     this.lyricContent = content;
-    const lines = content.split('\n').map(line => line.trim());
-    const lyric = this.parser.parse(lines);
+    const lines: string[] = content.split('\n').map((line: string) => line.trim());
+    const lyric: Lyric = this.parser.parse(lines);
     this.lyricController.setLyric(lyric);
     this.lyricControllerXF.setLyric(lyric);
     this.lyricControllerSingle.setLyric(lyric);
@@ -2091,137 +2117,14 @@ export struct LocalMusic {
   }
 
 
-  doSortType(index: number) {
-    switch (index) {
-      case 0:
-        this.videoLocalList.sort((a: VideoItem, b: VideoItem) => {
-          // 类型排序优先级
-          const typeOrder = getTypeOrder(a.type) - getTypeOrder(b.type);
-          if (typeOrder !== 0) {
-            return typeOrder;
-          }
-
-          // 处理艺术家可能为undefined的字符串比较
-          const artistA = a.artist?.trim() || ''; // 可选添加 trim() 处理空格
-          const artistB = b.artist?.trim() || '';
-          return artistA.localeCompare(artistB);
-        });
-
-        break;
-      case 1:
-        this.videoLocalList.sort((a: VideoItem, b: VideoItem) => {
-          // 类型排序优先级
-          const typeOrder = getTypeOrder(a.type) - getTypeOrder(b.type);
-          if (typeOrder !== 0) {
-            return typeOrder;
-          }
-
-          // 处理艺术家可能为undefined的字符串比较
-          const artistA = a.artist?.trim() || ''; // 可选添加 trim() 处理空格
-          const artistB = b.artist?.trim() || '';
-          return artistB.localeCompare(artistA);
-        });
-        break;
-      case 2:
-        this.videoLocalList.sort((a: VideoItem, b: VideoItem) => {
-          // 类型排序优先级
-          const typeOrder = getTypeOrder(a.type) - getTypeOrder(b.type);
-          if (typeOrder !== 0) {
-            return typeOrder;
-          }
-
-          // 处理专辑可能为undefined的字符串比较
-          const albumA = a.album?.trim() || ''; // 可选添加 trim() 处理空格
-          const albumB = b.album?.trim() || '';
-          return albumA.localeCompare(albumB);
-        });
-
-        break;
-      case 3:
-        this.videoLocalList.sort((a: VideoItem, b: VideoItem) => {
-          // 类型排序优先级
-          const typeOrder = getTypeOrder(a.type) - getTypeOrder(b.type);
-          if (typeOrder !== 0) {
-            return typeOrder;
-          }
-
-          // 处理专辑可能为undefined的字符串比较
-          const albumA = a.album?.trim() || ''; // 可选添加 trim() 处理空格
-          const albumB = b.album?.trim() || '';
-          return albumB.localeCompare(albumA);
-        });
-
-        break;
-      case 4:
-        Utility.doSortListAscending(this.videoLocalList,this.isShowFileName)
-        break;
-      case 5:
-        Utility.doSortListDescending(this.videoLocalList,this.isShowFileName)
-        break;
-      case 6:
-        this.videoLocalList.sort((a, b) => {
-          // First, sort by type
-          const typeOrder = getTypeOrder(a.type) - getTypeOrder(b.type);
-          if (typeOrder !== 0) {
-            return typeOrder;
-          }
-
-          // If types are the same, sort by cTime in ascending order
-          return a.cTime.localeCompare(b.cTime);
-        });
-        break;
-      case 7:
-        this.videoLocalList.sort((a, b) => {
-          // First, sort by type
-          const typeOrder = getTypeOrder(a.type) - getTypeOrder(b.type);
-          if (typeOrder !== 0) {
-            return typeOrder;
-          }
-
-          // If types are the same, sort by cTime in descending order
-          return b.cTime.localeCompare(a.cTime);
-        });
-        break;
-      case 8:
-        this.videoLocalList.sort((a:  VideoItem, b: VideoItem): number => {
-          // 1. Sort by type first
-          const typeOrder: number = getTypeOrder(a.type)  - getTypeOrder(b.type);
-          if (typeOrder !== 0) {
-            return typeOrder;
-          }
-
-          // 2. Then sort by artist count (numeric comparison)
-          let  sortMap:Map<string, VideoItem[]>= new Map<string, VideoItem[]>();
-          if(this.modeType === 2){
-            sortMap = this.artistMap
-          }else{
-            sortMap = this.albumMap
-          }
-          const aArtistCount: number = sortMap.get(a.name)?.length  || 0;
-          const bArtistCount: number = sortMap.get(b.name)?.length  || 0;
-          return aArtistCount - bArtistCount;
-        });
-        break;
-      case 9:
-        this.videoLocalList.sort((a:  VideoItem, b: VideoItem): number => {
-          // 1. Sort by type first
-          const typeOrder: number = getTypeOrder(a.type)  - getTypeOrder(b.type);
-          if (typeOrder !== 0) {
-            return typeOrder;
-          }
-
-          let  sortMap:Map<string, VideoItem[]>= new Map<string, VideoItem[]>();
-          if(this.modeType === 2){
-            sortMap = this.artistMap
-          }else{
-            sortMap = this.albumMap
-          }
-          const aArtistCount: number = sortMap.get(a.name)?.length  || 0;
-          const bArtistCount: number = sortMap.get(b.name)?.length  || 0;
-          return bArtistCount - aArtistCount;
-        });
-        break;
-    }
+  async doSortType(index: number): Promise<void> {
+    Logger.info('heanup LocalMusic', `doSortType: 切换排序方式 sortType=${index}, mode=${this.modeType}`);
+    
+    this.sortType = index;
+    PreferencesUtil.putSync(SettingPage.SORT_TYPE, index);
+    
+    // 分页模式下,排序需要重新从数据库查询
+    await this.resetAndLoadFirstPage();
   }
 
   showSheelDialog() {
@@ -2970,7 +2873,7 @@ export struct LocalMusic {
                 if(this.isGridMusic){
                   this.getGridView()
                 }else {
-                  this.getList()
+                  this.getListView()
                 }
               }
               if (this.modeType ==0||this.modeType==4) {
@@ -4888,41 +4791,26 @@ export struct LocalMusic {
   @State isSearchMode: boolean = false
 
   // 实时搜索逻辑(带防抖)
-  private onSearchInput(value: string) {
+  private async onSearchInput(value: string): Promise<void> {
     this.searchText = value.trim();
-    let mSearchList: Array<VideoItem> = []
-    switch (this.modeType) {
-      case 0:
-      case 1:
-        mSearchList = this.mediaKuList
-        break;
-      case 2:
-        mSearchList = this.artistList
-        break;
-      case 3:
-        mSearchList = this.albumList
-        break;
-      default :
-        mSearchList = this.videoLocalList
-        break;
-
-    }
-    // 新增条件判断:空输入时显示所有数据
+    
+    Logger.info('heanup LocalMusic', `onSearchInput: keyword="${this.searchText}", mode=${this.modeType}`);
+    
+    // 新增条件判断:空输入时退出搜索模式
     if (this.searchText === '') {
-      this.filteredList = [...mSearchList]; // 创建新数组以触发状态更新
-    } else {
-      this.filteredList = mSearchList.filter((item: VideoItem) => {
-        //支持模糊匹配和艺术家 专辑匹配
-        const regex = new RegExp(this.searchText.toLowerCase().replace(/\s+/g,  '.*'), 'i');
-        return regex.test(item.name.toLowerCase())||
-        regex.test(item.fileName?.toLowerCase()  ?? "") ||
-        regex.test(item.artist?.toLowerCase()  ?? "") ||
-        regex.test(item.album?.toLowerCase()  ?? "")
-      });
+      this.isSearchMode = false;
+      // 重新加载第一页(不带搜索条件)
+      await this.resetAndLoadFirstPage();
+      return;
     }
-
-    this.updateListData(this.filteredList);
-    // this.isSearchMode  = false; // 根据业务需求决定是否启用
+    
+    // 进入搜索模式
+    if (!this.isSearchMode) {
+      this.isSearchMode = true;
+    }
+    
+    // 使用数据库分页查询(带搜索条件)
+    await this.resetAndLoadFirstPage();
   }
 
   @State dragItem: number = -1
@@ -4966,7 +4854,7 @@ export struct LocalMusic {
   private oldColumn: number = this.columns;
   private pinchTime: number = 0;
   // 根据缩放阈值改变列数,触发WaterFlow重新布局
-  changeColumns(scale: number) {
+  changeColumns(scale: number): void {
     if (scale > (this.columns / (this.columns - 0.5)) && this.columns > 1) {
       this.columns--;
       this.columnChanged = true;
@@ -6202,6 +6090,25 @@ export struct LocalMusic {
 
       }, (item: VideoItem) => item.filePath + '_' + this.listRefreshKey)
 
+      // 加载中指示器
+      if (this.isLoadingPage) {
+        ListItem() {
+          Row() {
+            LoadingProgress()
+              .width(30)
+              .height(30)
+              .color(this.themeColor)
+            Text('加载中...')
+              .margin({ left: 10 })
+              .fontSize(14)
+              .fontColor($r('app.color.text_color'))
+          }
+          .width('100%')
+          .justifyContent(FlexAlign.Center)
+          .padding(20)
+        }
+      }
+
     }
     .onScrollStart(() => {
       this.isScrolling = true
@@ -6219,6 +6126,13 @@ export struct LocalMusic {
       }
       this.isShowAlphaBet = false
     })
+    .onReachEnd(() => {
+      // 触底加载下一页
+      Logger.info('heanup LocalMusic', `onReachEnd: hasMore=${this.hasMoreData}, isLoading=${this.isLoadingPage}`);
+      if (this.hasMoreData && !this.isLoadingPage) {
+        this.loadNextPage();
+      }
+    })
     .scrollBar(BarState.Off)
     .scale({ x: this.scaleValue, y: this.scaleValue, z: 1 })
     .gesture(PinchGesture({ fingers: 2 })
@@ -6597,12 +6511,23 @@ export struct LocalMusic {
           // 确保播放列表是完整的歌单歌曲列表(已在finishLoadingPlaylist中设置)
           Logger.info(`heanup isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
         }else {
-          let globalVideoList = Utility.getGlobalList(this.videoLocalList, CommonConstants.TYPE_LOCAL) as VideoItem[];
+          // 延迟加载完整播放列表
+          let globalVideoList: VideoItem[] = await this.loadFullSongList();
+          
+          // 如果延迟加载失败或返回空列表,回退到原逻辑
+          if (globalVideoList.length === 0) {
+            globalVideoList = Utility.getGlobalList(this.videoLocalList, CommonConstants.TYPE_LOCAL) as VideoItem[];
+            Logger.info(`heanup doPlay回退到videoLocalList - 列表长度: ${globalVideoList.length}`);
+          } else {
+            Logger.info(`heanup doPlay使用完整播放列表 - 列表长度: ${globalVideoList.length}`);
+          }
+          
           this.curIndex = Utility.getCurIndexFromGlobalList(globalVideoList, item.filePath)
           this.songList = globalVideoList
 
           this.sonDataSource.pushArrayData(this.songList)
           this.currentSong = globalVideoList[this.curIndex]
+          Logger.info(`heanup doPlay设置播放列表 - 总歌曲数: ${this.songList.length}, 当前索引: ${this.curIndex}, 当前歌曲: ${this.currentSong?.name}`)
         }
 
         AppStorage.setOrCreate('currentSong',this.currentSong) ;
@@ -6851,23 +6776,26 @@ export struct LocalMusic {
   @State discStr: string  = ''
 
   doUpdateData() {
-    setTimeout(() => {
+    setTimeout(async () => {
 
       if (this.modeType === 0) {
         this.deleteCache(this.currentPath)
-        this.getSortedFiles(this.currentPath)
+        await this.resetAndLoadFirstPage();
       }else if (this.modeType === 1){
+        await this.resetAndLoadFirstPage();
         workerInstance.postMessage({ code: 2, data: this.context,data2:this.packName });
       }
     }, 500)
   }
 
   doAllUpdateData() {
-    setTimeout(() => {
+    setTimeout(async () => {
 
       if (this.modeType === 0) {
         this.deleteCache(this.currentPath)
-        this.getSortedFiles(this.currentPath)
+        await this.resetAndLoadFirstPage();
+      }else{
+        await this.resetAndLoadFirstPage();
       }
       workerInstance.postMessage({ code: 2, data: this.context,data2:this.packName });
       workerInstance.postMessage({ code: 3, data: this.context });
@@ -6876,6 +6804,272 @@ export struct LocalMusic {
     }, 999)
   }
 
+  /**
+   * 加载指定页的数据
+   */
+  private async loadPage(pageIndex: number, append: boolean = true): Promise<void> {
+    try {
+      // 1. 检查缓存
+      if (this.pageCache.has(pageIndex)) {
+        Logger.info('heanup LocalMusic', `loadPage: 从缓存加载 page=${pageIndex}`);
+        const cachedData = this.pageCache.get(pageIndex)!;
+        if (append) {
+          this.videoLocalList = [...this.videoLocalList, ...cachedData];
+        } else {
+          this.videoLocalList = cachedData;
+        }
+        this.dataSource.pushArrayData(this.videoLocalList);
+        return;
+      }
+
+      this.isLoadingPage = true;
+      Logger.info('heanup LocalMusic', `loadPage: 开始加载 page=${pageIndex}, size=${this.pageSize}, mode=${this.modeType}`);
+      
+      // 2. 构造查询参数
+      const options: PageQueryOptions = {
+        pageIndex: pageIndex,
+        pageSize: this.pageSize,
+        sortType: this.sortType,
+        searchKeyword: this.isSearchMode ? this.searchText : undefined
+      };
+
+      // 根据 modeType 补充查询条件
+      if (this.modeType === 0) {
+        options.parentPath = this.currentPath;
+      }
+
+      // 3. 执行查询
+      let result: PageQueryResult = { items: [], totalCount: 0, hasMore: false };
+      switch (this.modeType) {
+        case 0:
+          result = await this.table.queryByParentPathPaged(options);
+          // modeType=0时,第一页需要添加文件夹列表
+          if (pageIndex === 0 && !this.isSearchMode) {
+            const directories: VideoItem[] = await this.loadDirectories(this.currentPath);
+            result.items = [...directories, ...result.items];
+            result.totalCount += directories.length;
+          }
+          break;
+        case 1:
+          result = await this.table.queryMediaLibraryPaged(options);
+          break;
+        case 2:
+          result = await this.table.queryArtistsPaged(options);
+          break;
+        case 3:
+          result = await this.table.queryAlbumsPaged(options);
+          break;
+        default:
+          result = await this.table.queryMediaLibraryPaged(options);
+          break;
+      }
+
+      if (!result || result.items.length === 0) {
+        Logger.info('heanup LocalMusic', 'loadPage: 查询结果为空');
+        this.isLoadingPage = false;
+        this.hasMoreData = false;
+        // 即使为空也要更新UI,显示空状态
+        this.totalCount = 0;
+        this.videoLocalList = [];
+        this.dataSource.pushArrayData(this.videoLocalList);
+        this.setButtonStatus();
+        return;
+      }
+
+      // 4. 更新状态和缓存
+      this.totalCount = result.totalCount;
+      this.hasMoreData = result.hasMore;
+      
+      Logger.info('heanup LocalMusic', `loadPage: 查询完成 items=${result.items.length}, total=${result.totalCount}, hasMore=${result.hasMore}`);
+      
+      // 缓存管理 - 只保留最近3页
+      this.pageCache.set(pageIndex, result.items);
+      this.cleanOldCache(pageIndex);
+
+      // 5. 更新显示列表
+      if (append) {
+        this.videoLocalList = [...this.videoLocalList, ...result.items];
+      } else {
+        this.videoLocalList = result.items;
+      }
+      
+      this.dataSource.pushArrayData(this.videoLocalList);
+      this.setButtonStatus();
+      this.setAlphaBet();
+      
+      this.isLoadingPage = false;
+    } catch (err) {
+      const error = err as Error;
+      Logger.error('heanup LocalMusic', `loadPage error: ${error.message}`);
+      ToastUtil.showToast('加载数据失败');
+      this.isLoadingPage = false;
+      this.hasMoreData = false;
+    }
+  }
+
+  /**
+   * 清理旧缓存 - 只保留当前页前后各1页
+   */
+  private cleanOldCache(currentPage: number): void {
+    const pagesToKeep = new Set([currentPage - 1, currentPage, currentPage + 1]);
+    const keysToDelete: number[] = [];
+    
+    this.pageCache.forEach((_, page) => {
+      if (!pagesToKeep.has(page)) {
+        keysToDelete.push(page);
+      }
+    });
+    
+    keysToDelete.forEach(page => this.pageCache.delete(page));
+    
+    if (keysToDelete.length > 0) {
+      Logger.info('heanup LocalMusic', `cleanOldCache: 清理了 ${keysToDelete.length} 页缓存`);
+    }
+  }
+
+  /**
+   * 加载文件夹列表(仅用于modeType=0的首页模式)
+   */
+  private async loadDirectories(curPath: string): Promise<VideoItem[]> {
+    try {
+      Logger.info('heanup LocalMusic', `loadDirectories: 开始加载文件夹 path=${curPath}`);
+      
+      // 检查缓存
+      const cacheKey = curPath + "_dirList";
+      const cachedDirList = this.findCache(cacheKey);
+      if (cachedDirList !== undefined && Array.isArray(cachedDirList)) {
+        Logger.info('heanup LocalMusic', `loadDirectories: 从缓存加载 count=${cachedDirList.length}`);
+        return cachedDirList as VideoItem[];
+      }
+      
+      // 从文件系统读取
+      const fileList = FileUtil.listFileSync(curPath);
+      const directories: VideoItem[] = [];
+      
+      for (let i = 0; i < fileList.length; i++) {
+        const path = curPath + '/' + fileList[i];
+        if (FileUtil.isDirectory(path)) {
+          const item: VideoItem = new VideoItem(
+            fileList[i].toString(), 
+            fileList[i].toString(), 
+            path, 
+            CommonConstants.TYPE_IS_DIR, 
+            0, 
+            ''
+          );
+          if (this.isShowDir(item.name)) {
+            directories.push(item);
+          }
+        }
+      }
+      
+      // 排序
+      Utility.doSortListAscending(directories);
+      
+      // 缓存文件夹列表
+      if (curPath === this.rootPath) {
+        this.dirList = directories;
+        this.addCache(cacheKey, directories);
+      }
+      
+      Logger.info('heanup LocalMusic', `loadDirectories: 加载完成 count=${directories.length}`);
+      return directories;
+    } catch (err) {
+      const error = err as Error;
+      Logger.error('heanup LocalMusic', `loadDirectories error: ${error.message}`);
+      return [];
+    }
+  }
+
+  /**
+   * 延迟加载完整播放列表(仅在播放时调用)
+   * 一次性加载当前模式/文件夹下的所有音乐文件
+   */
+  private async loadFullSongList(): Promise<VideoItem[]> {
+    // 如果已经加载过,直接返回
+    if (this.isFullSongListLoaded && this.fullSongList.length > 0) {
+      Logger.info('heanup LocalMusic', `loadFullSongList: 使用缓存 列表长度=${this.fullSongList.length}`);
+      return this.fullSongList;
+    }
+
+    try {
+      Logger.info('heanup LocalMusic', `loadFullSongList: 开始加载完整列表 mode=${this.modeType}`);
+      
+      const options: PageQueryOptions = {
+        pageIndex: 0,
+        pageSize: 10000,  // 一次性加载全部(设置足够大的值)
+        sortType: this.sortType,
+        searchKeyword: this.isSearchMode ? this.searchText : undefined
+      };
+
+      if (this.modeType === 0) {
+        options.parentPath = this.currentPath;
+      }
+
+      let result: PageQueryResult = { items: [], totalCount: 0, hasMore: false };
+      
+      switch (this.modeType) {
+        case 0:
+          result = await this.table.queryByParentPathPaged(options);
+          break;
+        case 1:
+          result = await this.table.queryMediaLibraryPaged(options);
+          break;
+        case 2:
+          result = await this.table.queryArtistsPaged(options);
+          break;
+        case 3:
+          result = await this.table.queryAlbumsPaged(options);
+          break;
+        default:
+          result = await this.table.queryMediaLibraryPaged(options);
+          break;
+      }
+
+      // 过滤出音乐文件(排除文件夹、艺术家、专辑等)
+      this.fullSongList = result.items.filter((item: VideoItem) => 
+        item.type !== CommonConstants.TYPE_IS_DIR && 
+        item.type !== CommonConstants.TYPE_IS_ARTIST && 
+        item.type !== CommonConstants.TYPE_IS_ALBUM
+      );
+      
+      this.isFullSongListLoaded = true;
+      Logger.info('heanup LocalMusic', `loadFullSongList: 加载完成 总歌曲数=${this.fullSongList.length}`);
+      
+      return this.fullSongList;
+    } catch (err) {
+      const error = err as Error;
+      Logger.error('heanup LocalMusic', `loadFullSongList error: ${error.message}`);
+      return [];
+    }
+  }
+
+  /**
+   * 重置分页状态并加载首页
+   */
+  private async resetAndLoadFirstPage(): Promise<void> {
+    Logger.info('heanup LocalMusic', 'resetAndLoadFirstPage: 重置分页状态');
+    this.currentPage = 0;
+    this.videoLocalList = [];
+    this.fullSongList = [];  // 清空完整播放列表
+    this.isFullSongListLoaded = false;  // 重置加载标记
+    this.pageCache.clear();
+    this.hasMoreData = true;
+    this.totalCount = 0;
+    await this.loadPage(0, false);
+  }
+
+  /**
+   * 加载下一页
+   */
+  private async loadNextPage(): Promise<void> {
+    if (this.hasMoreData && !this.isLoadingPage) {
+      this.currentPage++;
+      Logger.info('heanup LocalMusic', `loadNextPage: 加载第 ${this.currentPage} 页`);
+      await this.loadPage(this.currentPage, true);
+    }
+  }
+
   //编辑信息
   async doEdit(item: VideoItem) {
     if (!item) {
@@ -9319,7 +9513,7 @@ export struct LocalMusic {
       console.log("onecold 找到内嵌歌词 neiqianLrc =" + neiqianLrc)
       // Navidrome JSON歌词转换
       if (this.currentSong && isNavidromeType(this.currentSong.type)) {
-        const convertedLyric = LyricUtil.convertNavidromeJsonLyricToLrc(neiqianLrc);
+        const convertedLyric: string | undefined = LyricUtil.convertNavidromeJsonLyricToLrc(neiqianLrc);
         if (convertedLyric) {
           neiqianLrc = convertedLyric;
           console.log("onecold Navidrome歌词转换成功");
@@ -9477,8 +9671,8 @@ export struct LocalMusic {
     }
 
     this.lyricContent = lyricText;
-    let lines = lyricText.split('\n').map(line => line.trim());
-    let lyric = this.parser.parse(lines);
+    const lines: string[] = lyricText.split('\n').map((line: string) => line.trim());
+    const lyric: Lyric = this.parser.parse(lines);
 
     this.lyricController.setLyric(lyric);
     this.lyricControllerXF.setLyric(lyric);