Przeglądaj źródła

专辑增加按碟号-音轨号排序

onecold 6 miesięcy temu
rodzic
commit
9ace4a94a3

+ 80 - 6
entry/src/main/ets/common/util/MediaTable.ets

@@ -627,7 +627,7 @@ export default  class MediaTable {
       return await new Promise((resolve, reject) => {
         this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
           try {
-            const result = this.parseResultSetToVideoItems(resultSet);
+            const result = this.parseResultSetToVideoItems(resultSet, sortType);
             resolve(result);
           } catch (err) {
             reject(err);
@@ -653,7 +653,7 @@ export default  class MediaTable {
       return await new Promise((resolve, reject) => {
         this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
           try {
-            const result = this.parseResultSetToVideoItems(resultSet);
+            const result = this.parseResultSetToVideoItems(resultSet, sortType);
             resolve(result);
           } catch (err) {
             reject(err);
@@ -687,8 +687,11 @@ export default  class MediaTable {
     return Array.from(uniqueValues);   // Set转数组
   }
 
-  // 将ResultSet解析为VideoItem数组(复用原有逻辑)
-  private parseResultSetToVideoItems(resultSet: relationalStore.ResultSet): VideoItem[] {
+  /**
+   * 解析ResultSet为VideoItem数组(复用原有逻辑)
+   * 当sortType=10时,会按照disc和track进行二次排序
+   */
+  private parseResultSetToVideoItems(resultSet: relationalStore.ResultSet, sortType?: number): VideoItem[] {
     const items: VideoItem[] = [];
 
     try {
@@ -708,9 +711,67 @@ export default  class MediaTable {
       }
     }
 
+    // sortType=10: 按碟片编号(disc)和音轨号(track)升序排序
+    if (sortType === 10) {
+      items.sort((a, b) => {
+        // 解析disc字段,处理可能为空或包含非数字字符的情况
+        const discA = this.parseDiscNumber(a.disc);
+        const discB = this.parseDiscNumber(b.disc);
+
+        // 先按碟片编号排序
+        if (discA !== discB) {
+          return discA - discB;
+        }
+
+        // 碟片编号相同,按音轨号排序
+        const trackA = this.parseTrackNumber(a.track);
+        const trackB = this.parseTrackNumber(b.track);
+        return trackA - trackB;
+      });
+      Logger.info(RdbUtils.RDB_TAG, `parseResultSetToVideoItems: 已按碟片-音轨排序,歌曲数量=${items.length}`);
+    }
+
     return items;
   }
 
+  /**
+   * 解析碟片编号,返回数字进行比较
+   * 支持格式: "1", "1/5", "1.5", "CD1", "Vol1"等
+   */
+  private parseDiscNumber(disc: string | undefined | null): number {
+    if (!disc) return 0;
+
+    // 移除空格并转大写
+    const discStr = disc.toString().trim().toUpperCase();
+
+    // 尝试提取数字
+    const match = discStr.match(/(\d+)/);
+    if (match) {
+      return parseInt(match[1], 10);
+    }
+
+    return 0;
+  }
+
+  /**
+   * 解析音轨号,返回数字进行比较
+   * 支持格式: "1", "1/10", "1.12", "Track 1"等
+   */
+  private parseTrackNumber(track: string | undefined | null): number {
+    if (!track) return 0;
+
+    // 移除空格并转大写
+    const trackStr = track.toString().trim().toUpperCase();
+
+    // 尝试提取数字
+    const match = trackStr.match(/(\d+)/);
+    if (match) {
+      return parseInt(match[1], 10);
+    }
+
+    return 0;
+  }
+
   // 根据filePath更新lastPlayedStr的值同时playCount值加1
   public updateLastPlayedStrByFilePath(filePath: string, lastPlayedStr: string, callback: (success: boolean, error?: string) => void) {
     const normalizedPath = normalizeFilePath(filePath);
@@ -1195,6 +1256,7 @@ export default  class MediaTable {
   /**
    * 应用排序条件到查询谓词
    * sortType: 0-艺术家升序 1-艺术家降序 2-专辑升序 3-专辑降序 4-名称升序 5-名称降序 6-时间升序 7-时间降序
+   * 10-碟片编号升序+音轨号升序(用于专辑和文件夹)
    * @param predicates 查询谓词
    * @param sortType 排序类型
    * @param isShowFileName 是否显示文件名(当sortType为4或5时,此参数决定按name还是fileName排序)
@@ -1208,6 +1270,18 @@ export default  class MediaTable {
       nameColumn = DB_COLUMNS.FILE_NAME;
     }
 
+    // sortType=10 需要特殊处理:按碟片编号升序+音轨号升序
+    // 由于RdbPredicates不支持多字段排序,我们需要标记这个谓词需要使用原生SQL
+    if (type === 10) {
+      // 将排序类型存储在predicates的某个属性中,以便后续使用原生SQL查询
+      // 注意:这需要在查询时检查并使用querySql而不是query
+      Logger.info(RdbUtils.RDB_TAG, 'applySortConditions: 碟片-音轨排序,需要使用原生SQL');
+      // 由于无法直接修改predicates,我们使用单字段排序作为fallback
+      predicates.orderByAsc('disc'); // 先按碟片编号排序
+      // 音轨号排序需要在查询后进行内存排序
+      return;
+    }
+
     switch (type) {
       case 0:
         predicates.orderByAsc(DB_COLUMNS.ARTIST);
@@ -1303,7 +1377,7 @@ export default  class MediaTable {
       return new Promise((resolve, reject) => {
         this.accountTable.query(pagePredicates, (resultSet: relationalStore.ResultSet) => {
           try {
-            const items = this.parseResultSetToVideoItems(resultSet);
+            const items = this.parseResultSetToVideoItems(resultSet, options.sortType);
             const hasMore = (offset + items.length) < totalCount;
             Logger.info('heanup MediaTable', `queryByParentPathPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
             resolve({ items, totalCount, hasMore });
@@ -1359,7 +1433,7 @@ export default  class MediaTable {
       return new Promise((resolve, reject) => {
         this.accountTable.query(pagePredicates, (resultSet: relationalStore.ResultSet) => {
           try {
-            const items = this.parseResultSetToVideoItems(resultSet);
+            const items = this.parseResultSetToVideoItems(resultSet, options.sortType);
             const hasMore = (offset + items.length) < totalCount;
             Logger.info('heanup MediaTable', `queryMediaLibraryPaged: 查询完成 items=${items.length}, total=${totalCount}, hasMore=${hasMore}`);
             resolve({ items, totalCount, hasMore });

+ 136 - 20
entry/src/main/ets/view/LocalMusic.ets

@@ -703,8 +703,10 @@ export struct LocalMusic {
     }
     this.isZero = false
     if (this.modeType !== 0 && this.isCanBack) {
-      this.onModeChange()
+      // 先设置 isCanBack=false,确保 onModeChange 读取的是列表页的排序设置
       this.isCanBack = false
+      this.onModeChange()
+
       // 返回专辑列表时恢复滚动偏移量,支持列表和网格
       setTimeout(() => {
         if (this.modeType == 2) {
@@ -2269,6 +2271,15 @@ export struct LocalMusic {
         .onClick(async () => {
           await this.doSortType(7)
         })
+      MenuItem({
+        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.music')),
+        content: '按碟片编号-音轨号升序'
+      })
+        .onClick(async () => {
+          await this.doSortType(10)
+        })
+        .visibility((this.modeType==0||(this.modeType==3&&this.isCanBack))?Visibility.Visible:Visibility.None)
+
     }.attributeModifier(new MenuModifier())
   }
 
@@ -2305,48 +2316,119 @@ export struct LocalMusic {
         .onClick(async () => {
           await this.doSortType(9)
         })
+
     }.attributeModifier(new MenuModifier())
   }
 
 
   async doSortType(index: number): Promise<void> {
-    Logger.info('heanup LocalMusic', `doSortType: 切换排序方式 sortType=${index}, mode=${this.modeType}`);
+    Logger.info('heanup LocalMusic', `doSortType: 切换排序方式 sortType=${index}, mode=${this.modeType}, isCanBack=${this.isCanBack}`);
 
     this.sortType = index;
     this.saveSortTypeForCurrentMode(index);
 
-    // 分页模式下,排序需要重新从数据库查询
-    await this.resetAndLoadFirstPage();
+    // 详情页模式(modeType=2/3 且 isCanBack=true): 重新查询当前项目的歌曲
+    if (this.isCanBack && (this.modeType === 2 || this.modeType === 3)) {
+      await this.reloadCurrentDetailSongs();
+    } else {
+      // 分页模式下,排序需要重新从数据库查询
+      await this.resetAndLoadFirstPage();
+    }
+  }
+
+  /**
+   * 重新加载当前详情页(专辑/艺术家)的歌曲数据
+   * 用于详情页切换排序方式时重新查询并排序
+   */
+  private async reloadCurrentDetailSongs(): Promise<void> {
+    Logger.info('heanup LocalMusic', `reloadCurrentDetailSongs: modeType=${this.modeType}, titleName=${this.titleName}, sortType=${this.sortType}`);
+
+    try {
+      if (this.modeType === 2) {
+        // 艺术家详情页: 重新查询该艺术家的歌曲
+        const mList = await this.table.querySongsByArtist(this.titleName, this.sortType, this.isShowFileName);
+        if (ArrayUtil.isNotEmpty(mList)) {
+          // 使用带排序类型的缓存键
+          const cacheKey = `${this.titleName}_sort_${this.sortType}`;
+          this.artistMap.set(cacheKey, mList);
+          this.totalCount = mList.length;
+          // 使用 noSort=true 避免在 updateListData 中重新排序导致死循环
+          this.updateListData(mList, true);
+          Logger.info('heanup LocalMusic', `reloadCurrentDetailSongs: 艺术家详情页重新加载完成, 歌曲数=${mList.length}`);
+        }
+      } else if (this.modeType === 3) {
+        // 专辑详情页: 重新查询该专辑的歌曲
+        const albumList = await this.table.querySongsByAlbum(this.titleName, this.sortType, this.isShowFileName);
+        if (ArrayUtil.isNotEmpty(albumList)) {
+          // 使用带排序类型的缓存键
+          const cacheKey = `${this.titleName}_sort_${this.sortType}`;
+          this.albumMap.set(cacheKey, albumList);
+          this.totalCount = albumList.length;
+          // 使用 noSort=true 避免在 updateListData 中重新排序导致死循环
+          this.updateListData(albumList, true);
+          Logger.info('heanup LocalMusic', `reloadCurrentDetailSongs: 专辑详情页重新加载完成, 歌曲数=${albumList.length}`);
+        }
+      }
+    } catch (err) {
+      const error = err as Error;
+      Logger.error('heanup LocalMusic', `reloadCurrentDetailSongs error: ${error.message}`);
+      ToastUtil.showToast('重新加载数据失败');
+    }
   }
 
   /**
    * 根据当前模式保存排序类型到不同的存储键
+   * 区分列表页和详情页的排序设置
    */
   private saveSortTypeForCurrentMode(sortType: number): void {
     let storageKey = SettingPage.SORT_TYPE;
     if (this.modeType === 2) {
-      storageKey = 'SORT_TYPE_ARTIST';
+      // 艺术家模式
+      if (this.isCanBack) {
+        storageKey = 'SORT_TYPE_ARTIST_DETAIL'; // 艺术家详情页排序
+      } else {
+        storageKey = 'SORT_TYPE_ARTIST'; // 艺术家列表页排序
+      }
     } else if (this.modeType === 3) {
-      storageKey = 'SORT_TYPE_ALBUM';
+      // 专辑模式
+      if (this.isCanBack) {
+        storageKey = 'SORT_TYPE_ALBUM_DETAIL'; // 专辑详情页排序
+      } else {
+        storageKey = 'SORT_TYPE_ALBUM'; // 专辑列表页排序
+      }
     } else {
       storageKey = SettingPage.SORT_TYPE;
     }
     PreferencesUtil.putSync(storageKey, sortType);
+    Logger.info('heanup LocalMusic', `saveSortTypeForCurrentMode: mode=${this.modeType}, isCanBack=${this.isCanBack}, sortType=${sortType}, key=${storageKey}`);
   }
 
   /**
    * 根据当前模式加载排序类型
+   * 区分列表页和详情页的排序设置
    */
   private loadSortTypeForCurrentMode(): number {
     let storageKey = SettingPage.SORT_TYPE;
     let defaultSortType = 4;
 
     if (this.modeType === 2) {
-      storageKey = 'SORT_TYPE_ARTIST';
-      defaultSortType = 9; // 艺术家模式默认按数量降序
+      // 艺术家模式
+      if (this.isCanBack) {
+        storageKey = 'SORT_TYPE_ARTIST_DETAIL'; // 艺术家详情页排序
+        defaultSortType = 4; // 详情页默认按名称升序
+      } else {
+        storageKey = 'SORT_TYPE_ARTIST'; // 艺术家列表页排序
+        defaultSortType = 9; // 列表页默认按数量降序
+      }
     } else if (this.modeType === 3) {
-      storageKey = 'SORT_TYPE_ALBUM';
-      defaultSortType = 9; // 专辑模式默认按数量降序
+      // 专辑模式
+      if (this.isCanBack) {
+        storageKey = 'SORT_TYPE_ALBUM_DETAIL'; // 专辑详情页排序
+        defaultSortType = 4; // 详情页默认按名称升序
+      } else {
+        storageKey = 'SORT_TYPE_ALBUM'; // 专辑列表页排序
+        defaultSortType = 9; // 列表页默认按数量降序
+      }
     }
 
     return PreferencesUtil.getNumberSync(storageKey, defaultSortType);
@@ -6679,15 +6761,32 @@ export struct LocalMusic {
           }
           this.mScrollMap.set('lastListArtistScrollOffset', offsetA)
         }
-        let mList = this.artistMap.get(item.name)
+
+        // 进入艺术家详情页前,先加载详情页的排序类型
+        // 注意:需要临时设置isCanBack=true,以便loadSortTypeForCurrentMode读取详情页的排序设置
+        const artistOriginalIsCanBack = this.isCanBack;
+        this.isCanBack = true;
+        const artistDetailSortType = this.loadSortTypeForCurrentMode();
+        this.isCanBack = artistOriginalIsCanBack; // 恢复原值
+        Logger.info('heanup LocalMusic', `进入艺术家详情页: 加载详情页排序 sortType=${artistDetailSortType}`);
+
+        // 缓存键需要包含排序类型,因为不同的排序类型会产生不同的数据
+        const artistCacheKey = `${item.name}_sort_${artistDetailSortType}`;
+        let mList = this.artistMap.get(artistCacheKey)
+
         if (mList === undefined || ArrayUtil.isEmpty(mList)) {
-          mList = await this.table.querySongsByArtist(item.name, this.sortType, this.isShowFileName);
+          // 使用详情页的排序类型查询
+          mList = await this.table.querySongsByArtist(item.name, artistDetailSortType, this.isShowFileName);
           if (ArrayUtil.isNotEmpty(mList)) {
-            this.artistMap.set(item.name, mList);
+            this.artistMap.set(artistCacheKey, mList);
           }
         }
+
+        // 设置当前排序类型为详情页排序
+        this.sortType = artistDetailSortType;
+
         if (mList !== undefined && ArrayUtil.isNotEmpty(mList)) {
-          Utility.doSortListAscending(mList)
+          // 注意:不再调用 Utility.doSortListAscending,因为数据已经按 artistDetailSortType 排序了
           const nextArtistCountMap = new Map(this.artistSongCountMap);
           nextArtistCountMap.set(item.name, mList.length);
           this.artistSongCountMap = nextArtistCountMap;
@@ -6701,7 +6800,7 @@ export struct LocalMusic {
           if (item.pixelMapPath) {
             this.currentTitleCover = item.pixelMapPath
           }
-          this.updateListData(mList)
+          this.updateListData(mList, true) // 使用 true 跳过排序
         } else {
           ToastUtil.showToast('未找到该艺术家的歌曲');
           return;
@@ -6751,15 +6850,32 @@ export struct LocalMusic {
           }
           this.mScrollMap.set('lastListAlBumScrollOffset', offset)
         }
-        let albumList = this.albumMap.get(item.name)
+
+        // 进入专辑详情页前,先加载详情页的排序类型
+        // 注意:需要临时设置isCanBack=true,以便loadSortTypeForCurrentMode读取详情页的排序设置
+        const albumOriginalIsCanBack = this.isCanBack;
+        this.isCanBack = true;
+        const albumDetailSortType = this.loadSortTypeForCurrentMode();
+        this.isCanBack = albumOriginalIsCanBack; // 恢复原值
+        Logger.info('heanup LocalMusic', `进入专辑详情页: 加载详情页排序 sortType=${albumDetailSortType}`);
+
+        // 缓存键需要包含排序类型,因为不同的排序类型会产生不同的数据
+        const albumCacheKey = `${item.name}_sort_${albumDetailSortType}`;
+        let albumList = this.albumMap.get(albumCacheKey)
+
         if (albumList === undefined || ArrayUtil.isEmpty(albumList)) {
-          albumList = await this.table.querySongsByAlbum(item.name, this.sortType, this.isShowFileName);
+          // 使用详情页的排序类型查询
+          albumList = await this.table.querySongsByAlbum(item.name, albumDetailSortType, this.isShowFileName);
           if (ArrayUtil.isNotEmpty(albumList)) {
-            this.albumMap.set(item.name, albumList);
+            this.albumMap.set(albumCacheKey, albumList);
           }
         }
+
+        // 设置当前排序类型为详情页排序
+        this.sortType = albumDetailSortType;
+
         if (albumList !== undefined && ArrayUtil.isNotEmpty(albumList)) {
-          Utility.doSortListAscending(albumList)
+          // 注意:不再调用 Utility.doSortListAscending,因为数据已经按 albumDetailSortType 排序了
           const nextAlbumCountMap = new Map(this.albumSongCountMap);
           nextAlbumCountMap.set(item.name, albumList.length);
           this.albumSongCountMap = nextAlbumCountMap;
@@ -6773,7 +6889,7 @@ export struct LocalMusic {
           if (item.pixelMapPath) {
             this.currentTitleCover = item.pixelMapPath
           }
-          this.updateListData(albumList)
+          this.updateListData(albumList, true) // 使用 true 跳过排序
           this.mScrollMap.set(item.name, this.selectedIndex)
           this.rightTopImage = $r('sys.symbol.chevron_left')
         } else {