onecold 9 mesi fa
parent
commit
b32350db11

+ 2 - 2
AppScope/app.json5

@@ -2,8 +2,8 @@
   "app": {
   "app": {
     "bundleName": "com.xgplayer.ttmusic.hm",
     "bundleName": "com.xgplayer.ttmusic.hm",
     "vendor": "example",
     "vendor": "example",
-    "versionCode": 20251105,
-    "versionName": "1.6.8",
+    "versionCode": 20251113,
+    "versionName": "1.6.9",
     "icon": "$media:app_icon",
     "icon": "$media:app_icon",
     "label": "$string:app_name",
     "label": "$string:app_name",
     "multiAppMode": {
     "multiAppMode": {

+ 7 - 0
entry/src/main/ets/common/constants/CommonConstants.ets

@@ -93,6 +93,13 @@ export class CommonConstants {
     ,'.aif','.au','.eac3','.mlp','.tak','.thd','.tta','.wv','.ac3','.amr','.mka','.mpc','.ra',
     ,'.aif','.au','.eac3','.mlp','.tak','.thd','.tta','.wv','.ac3','.amr','.mka','.mpc','.ra',
     '.asf','.m2ts','.m4b','.mts','.rm','.wtv','.dts','.av3a','.dsd','.dsf','.wav']
     '.asf','.m2ts','.m4b','.mts','.rm','.wtv','.dts','.av3a','.dsd','.dsf','.wav']
 
 
+  // 图片帧信息常量
+  static readonly IMAGE_FRAME_INFO: ImageFrameInfo[] = [
+    { src: $r("app.media.app_loading0") },
+    { src: $r("app.media.app_loading1") },
+    { src: $r("app.media.app_loading2") },
+    { src: $r("app.media.app_loading3") },
+  ];
 
 
   // 倍数格式支持列表
   // 倍数格式支持列表
   public static video_speed_list: number[] = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 3]
   public static video_speed_list: number[] = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 3]

+ 2 - 1
entry/src/main/ets/common/util/RemoteDriveManager.ets

@@ -944,7 +944,7 @@ export class RemoteDriveManager {
       videoItem.artist = musicData.artist
       videoItem.artist = musicData.artist
       videoItem.name = musicData.title
       videoItem.name = musicData.title
     }
     }
-
+    videoItem.album = ''
     // 设置WebDAV账号ID,用于后续认证信息查询
     // 设置WebDAV账号ID,用于后续认证信息查询
     if (account && account.id) {
     if (account && account.id) {
       videoItem.webdav_account_id = account.id.toString();
       videoItem.webdav_account_id = account.id.toString();
@@ -980,6 +980,7 @@ export class RemoteDriveManager {
       undefined,
       undefined,
       fileInfo.fileName
       fileInfo.fileName
     );
     );
+    videoItem.album = ''
     videoItem.size = Utility.formatFSize(fileInfo.contentLength);
     videoItem.size = Utility.formatFSize(fileInfo.contentLength);
     if (account && account.id) {
     if (account && account.id) {
       videoItem.webdav_account_id = account.id.toString();
       videoItem.webdav_account_id = account.id.toString();

+ 42 - 22
entry/src/main/ets/common/util/Utility.ets

@@ -1719,6 +1719,9 @@ const ARTIST_KEYWORDS = [
 /**
 /**
  * 智能解析音乐文件名
  * 智能解析音乐文件名
  */
  */
+/**
+ * 智能解析音乐文件名(改进版)
+ */
 export function parseMusicFileName(fileName: string): MusicInfo {
 export function parseMusicFileName(fileName: string): MusicInfo {
   const result: MusicInfo = new MusicInfo();
   const result: MusicInfo = new MusicInfo();
   if (!fileName) return result;
   if (!fileName) return result;
@@ -1727,7 +1730,7 @@ export function parseMusicFileName(fileName: string): MusicInfo {
   const cleanName: string = fileName.trim();
   const cleanName: string = fileName.trim();
   const lastDotIndex: number = cleanName.lastIndexOf('.');
   const lastDotIndex: number = cleanName.lastIndexOf('.');
   const baseName: string = lastDotIndex > 0 ?
   const baseName: string = lastDotIndex > 0 ?
-  cleanName.substring(0,  lastDotIndex).trim() :
+    cleanName.substring(0, lastDotIndex).trim() :
     cleanName;
     cleanName;
 
 
   // 支持的分隔符(明确定义类型)
   // 支持的分隔符(明确定义类型)
@@ -1744,55 +1747,72 @@ export function parseMusicFileName(fileName: string): MusicInfo {
   }
   }
 
 
   // 分割字符串
   // 分割字符串
-  if (bestSplitIndex > 0 && bestSplitIndex < baseName.length  - 1) {
-    let part1: string = baseName.substring(0,  bestSplitIndex).trim();
-    let part2: string = baseName.substring(bestSplitIndex  + 1).trim();
+  if (bestSplitIndex > 0 && bestSplitIndex < baseName.length - 1) {
+    let part1: string = baseName.substring(0, bestSplitIndex).trim();
+    let part2: string = baseName.substring(bestSplitIndex + 1).trim();
 
 
     // 处理前缀序号
     // 处理前缀序号
     part1 = part1.replace(/^\d+[\s\.\-- —~~]*/, '').trim();
     part1 = part1.replace(/^\d+[\s\.\-- —~~]*/, '').trim();
 
 
-    // 判断歌手部分(明确定义返回类型
+    // 判断歌手部分(改进版
     const identifyArtist = (str: string): boolean => {
     const identifyArtist = (str: string): boolean => {
-      return COMMON_CHINESE_SURNAMES.some((surname:  string) =>
-      str.startsWith(surname)  ||
+      // 检查是否包含常见歌手关键词
+      if (ARTIST_KEYWORDS.some((keyword: string) => str.includes(keyword))) {
+        return true;
+      }
+
+      // 检查是否以常见姓氏开头
+      return COMMON_CHINESE_SURNAMES.some((surname: string) =>
+      str.startsWith(surname) ||
       new RegExp(`[ ,,、&&]${surname}`).test(str)
       new RegExp(`[ ,,、&&]${surname}`).test(str)
-      ) || ARTIST_KEYWORDS.some((keyword:  string) =>
-      str.includes(keyword)
       );
       );
     };
     };
 
 
-    // 判断歌手位置
+    // 判断歌手位置(改进逻辑)
     const part1IsArtist: boolean = identifyArtist(part1);
     const part1IsArtist: boolean = identifyArtist(part1);
     const part2IsArtist: boolean = identifyArtist(part2);
     const part2IsArtist: boolean = identifyArtist(part2);
 
 
-    if (part1IsArtist && !part2IsArtist) {
-      result.artist  = part1;
-      result.title  = part2;
+    // 特殊处理:如果两个部分都识别为艺术家或都不是艺术家
+    if ((part1IsArtist && part2IsArtist) || (!part1IsArtist && !part2IsArtist)) {
+      // 基于常见模式判断:通常"歌曲名 - 歌手"格式更常见
+      // 或者根据长度判断,歌手名通常较短
+      if (part2.length <= part1.length && identifyArtist(part2)) {
+        result.artist = part2;
+        result.title = part1;
+      } else {
+        result.artist = part1;
+        result.title = part2;
+      }
+    } else if (part1IsArtist && !part2IsArtist) {
+      result.artist = part1;
+      result.title = part2;
     } else if (part2IsArtist && !part1IsArtist) {
     } else if (part2IsArtist && !part1IsArtist) {
-      result.artist  = part2;
-      result.title  = part1;
+      result.artist = part2;
+      result.title = part1;
     } else {
     } else {
-      result.artist  = part1.length  <= part2.length  ? part1 : part2;
-      result.title  = part1.length  <= part2.length  ? part2 : part1;
+      // 默认处理
+      result.artist = part1;
+      result.title = part2;
     }
     }
 
 
     // 后处理
     // 后处理
-    result.title  = result.title
+    result.title = result.title
       .replace(/(?:\(|()[^))]*(?:)|\))/g, '')
       .replace(/(?:\(|()[^))]*(?:)|\))/g, '')
       .replace(/\s*[—-]\s*(?:Live|Version|Remix|伴奏).*/i, '')
       .replace(/\s*[—-]\s*(?:Live|Version|Remix|伴奏).*/i, '')
       .trim();
       .trim();
 
 
     // 有效性验证
     // 有效性验证
-    result.isValid  = result.artist.length  > 0 &&
-      result.title.length  > 0;
+    result.isValid = result.artist.length > 0 &&
+      result.title.length > 0;
   } else {
   } else {
-    result.title  = baseName;
-    result.isValid  = result.title.length  > 0;
+    result.title = baseName;
+    result.isValid = result.title.length > 0;
   }
   }
 
 
   return result;
   return result;
 }
 }
 
 
+
 function getFileNameWithoutExtension(filePath: string): string {
 function getFileNameWithoutExtension(filePath: string): string {
   const fileName = filePath.split('/').pop()  || '';
   const fileName = filePath.split('/').pop()  || '';
   const lastDotIndex = fileName.lastIndexOf('.');
   const lastDotIndex = fileName.lastIndexOf('.');

+ 85 - 53
entry/src/main/ets/pages/SettingPage.ets

@@ -25,6 +25,7 @@ import { appInfoManager } from '@kit.StoreKit'
 // @Entry
 // @Entry
 @Component
 @Component
 export struct SettingPage {
 export struct SettingPage {
+  @State isNoJumpToHome: boolean = false //网盘播放不跳转首页
   @State iconCurrentID: string = 'default';//图标的id
   @State iconCurrentID: string = 'default';//图标的id
   @State iconArray: Array<Icon> = []
   @State iconArray: Array<Icon> = []
   @StorageProp('isLandscape')  isLandscape: boolean = false;
   @StorageProp('isLandscape')  isLandscape: boolean = false;
@@ -274,6 +275,8 @@ export struct SettingPage {
     this.isShowBackFast  = PreferencesUtil.getBooleanSync('isShowBackFast', false)
     this.isShowBackFast  = PreferencesUtil.getBooleanSync('isShowBackFast', false)
     this.fastForwardSeconds  = PreferencesUtil.getStringSync('fastForwardSeconds', '10')
     this.fastForwardSeconds  = PreferencesUtil.getStringSync('fastForwardSeconds', '10')
     this.isCopyFileToDownLoad = PreferencesUtil.getBooleanSync(SettingPage.IS_COPYFILE_TO_DOWNLOAD, false)
     this.isCopyFileToDownLoad = PreferencesUtil.getBooleanSync(SettingPage.IS_COPYFILE_TO_DOWNLOAD, false)
+    this.isNoJumpToHome = PreferencesUtil.getBooleanSync('isNoJumpToHome', true)
+
     // 只用 themeMode 控制主题
     // 只用 themeMode 控制主题
     this.themeMode = PreferencesUtil.getNumberSync(SettingPage.THEME_MODE, 0)
     this.themeMode = PreferencesUtil.getNumberSync(SettingPage.THEME_MODE, 0)
     this.applyThemeMode(this.themeMode)
     this.applyThemeMode(this.themeMode)
@@ -1162,59 +1165,59 @@ export struct SettingPage {
           .padding(0)
           .padding(0)
 
 
           // 网络工具
           // 网络工具
-          Column() {
-            Row() {
-              Text('网络工具')
-                .margin({ left: 18, right: 20 })
-                .fontSize(16)
-                .fontColor(Color.Gray)
-                .fontWeight(480)
-                .layoutWeight(1)
-            }
-            .height(48)
-
-            Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-            Button({ type: ButtonType.Normal, stateEffect: true }) {
-              Row() {
-                Image($r('app.media.cloudDisk'))
-                  .width(22)
-                  .height(22)
-                  .alignSelf(ItemAlign.Center)
-                  .margin({ left: 15 })
-                Text('SMB 测试工具')
-                  .margin({ left: 8 })
-                  .fontSize(15)
-                  .fontColor(Color.Gray)
-                  .fontWeight(480)
-                  .layoutWeight(1)
-                Image($r('app.media.arrow_right'))
-                  .width(22)
-                  .height(22)
-                  .margin({ right: 18 })
-              }
-            }
-            .height(55)
-            .backgroundColor(Color.Transparent)
-            .clickEffect({ level: ClickEffectLevel.HEAVY })
-            .onClick(() => {
-              router.pushUrl({
-                url: 'pages/SmbTestPage'
-              });
-            })
-
-            Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-          }
-          .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
-            .animation({ duration: 500, curve: Curve.Ease, delay: 260 }))
-          .backgroundColor($r('app.color.settings_background_main'))
-          .borderRadius(20)
-          .margin({
-            left: 0,
-            right: 0,
-            top: 0,
-            bottom: 10
-          })
-          .padding(0)
+          // Column() {
+          //   Row() {
+          //     Text('网络工具')
+          //       .margin({ left: 18, right: 20 })
+          //       .fontSize(16)
+          //       .fontColor(Color.Gray)
+          //       .fontWeight(480)
+          //       .layoutWeight(1)
+          //   }
+          //   .height(48)
+          //
+          //   Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
+          //   Button({ type: ButtonType.Normal, stateEffect: true }) {
+          //     Row() {
+          //       Image($r('app.media.cloudDisk'))
+          //         .width(22)
+          //         .height(22)
+          //         .alignSelf(ItemAlign.Center)
+          //         .margin({ left: 15 })
+          //       Text('SMB 测试工具')
+          //         .margin({ left: 8 })
+          //         .fontSize(15)
+          //         .fontColor(Color.Gray)
+          //         .fontWeight(480)
+          //         .layoutWeight(1)
+          //       Image($r('app.media.arrow_right'))
+          //         .width(22)
+          //         .height(22)
+          //         .margin({ right: 18 })
+          //     }
+          //   }
+          //   .height(55)
+          //   .backgroundColor(Color.Transparent)
+          //   .clickEffect({ level: ClickEffectLevel.HEAVY })
+          //   .onClick(() => {
+          //     router.pushUrl({
+          //       url: 'pages/SmbTestPage'
+          //     });
+          //   })
+          //
+          //   Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
+          // }
+          // .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
+          //   .animation({ duration: 500, curve: Curve.Ease, delay: 260 }))
+          // .backgroundColor($r('app.color.settings_background_main'))
+          // .borderRadius(20)
+          // .margin({
+          //   left: 0,
+          //   right: 0,
+          //   top: 0,
+          //   bottom: 10
+          // })
+          // .padding(0)
 
 
 
 
           // 音频设置分组
           // 音频设置分组
@@ -1557,6 +1560,35 @@ export struct SettingPage {
             .height(55)
             .height(55)
             .clickEffect({ level: ClickEffectLevel.HEAVY })
             .clickEffect({ level: ClickEffectLevel.HEAVY })
 
 
+
+            Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
+            // 网盘播放不跳转到首页
+            Row() {
+              SymbolGlyph($r('sys.symbol.close_sidebar'))
+                .fontSize(20)
+                .fontColor([this.themeColor])
+                .alignSelf(ItemAlign.Center)
+                .margin({ left: 15 })
+              Text('网盘播放不跳转到首页')
+                .margin({ left: 8 })
+                .fontSize(15)
+                .fontColor(Color.Gray)
+                .fontWeight(480)
+                .layoutWeight(1)
+              Toggle({ type: ToggleType.Switch, isOn: this.isNoJumpToHome })
+                .selectedColor(this.themeColor)
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
+                .switchPointColor(Color.White)
+                .margin({ right: 18 })
+                .onChange((checked: boolean) => {
+                  this.isNoJumpToHome = checked;
+                  PreferencesUtil.put('isNoJumpToHome', this.isNoJumpToHome)
+                })
+                .width(50)
+                .height(30);
+            }
+            .height(55)
+            .clickEffect({ level: ClickEffectLevel.HEAVY })
             Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
             Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
             // 长歌名滚动
             // 长歌名滚动
             Row() {
             Row() {

+ 202 - 44
entry/src/main/ets/pages/WebDavMainPage.ets

@@ -3,7 +3,7 @@ import { WebDavAccount } from '../viewmodel/WebDavAccount';
 import { Song } from '../viewmodel/Song';
 import { Song } from '../viewmodel/Song';
 import { RemoteDriveManagerStates } from '../common/enums/RemoteDriveManagerStates';
 import { RemoteDriveManagerStates } from '../common/enums/RemoteDriveManagerStates';
 import Logger from '../common/util/Logger';
 import Logger from '../common/util/Logger';
-import { promptAction, router, window } from '@kit.ArkUI';
+import { promptAction, SymbolGlyphModifier,router, window } from '@kit.ArkUI';
 import { CommonConstants } from '../common/constants/CommonConstants';
 import { CommonConstants } from '../common/constants/CommonConstants';
 import { VideoItem } from '../viewmodel/VideoItem';
 import { VideoItem } from '../viewmodel/VideoItem';
 import { GlobalContext } from '../common/util/GlobalContext';
 import { GlobalContext } from '../common/util/GlobalContext';
@@ -13,9 +13,12 @@ import { emitter } from '@kit.BasicServicesKit';
 import { EventConstants } from '../common/constants/EventConstants';
 import { EventConstants } from '../common/constants/EventConstants';
 import { LazyDataSource } from '../common/util/LazyDataSource';
 import { LazyDataSource } from '../common/util/LazyDataSource';
 import { PreferencesUtil } from '@pura/harmony-utils';
 import { PreferencesUtil } from '@pura/harmony-utils';
-import { ButtonFancyModifier, ShadowModifier, SymbolGlyphFancyModifier } from '../common/util/AttributeModifierUtil';
+import { ButtonFancyModifier,
+  MenuModifier,
+  ShadowModifier, SymbolGlyphFancyModifier } from '../common/util/AttributeModifierUtil';
 import { getRemoteDriveAccountLabel, getRemoteDriveDisplayLabel, getRemoteDrivePlaylistPrefix } from '../common/util/RemoteDriveLabel';
 import { getRemoteDriveAccountLabel, getRemoteDriveDisplayLabel, getRemoteDrivePlaylistPrefix } from '../common/util/RemoteDriveLabel';
 import { RemoteDriveType } from '../common/enums/RemoteDriveType';
 import { RemoteDriveType } from '../common/enums/RemoteDriveType';
+import { Utility } from '../common/util/Utility';
 
 
 /**
 /**
  * 歌单播放事件数据
  * 歌单播放事件数据
@@ -63,6 +66,10 @@ function decodeUrlEncodedString(encodedStr: string): string {
 @Entry
 @Entry
 @Component
 @Component
 export struct WebDavMainPage {
 export struct WebDavMainPage {
+  @StorageProp('animationState') animationState: AnimationStatus= AnimationStatus.Running
+  @State isNoJumpToHome: boolean = false //网盘播放不跳转首页
+  @State isSearchMode: boolean = false
+  @StorageLink('currentSong') currentSong: VideoItem | undefined = undefined;
   @StorageProp('topSafeHeight') topSafeHeight: number = 0;
   @StorageProp('topSafeHeight') topSafeHeight: number = 0;
   @State webdavManager: RemoteDriveManager = RemoteDriveManager.getInstance();
   @State webdavManager: RemoteDriveManager = RemoteDriveManager.getInstance();
   @State accounts: WebDavAccount[] = [];
   @State accounts: WebDavAccount[] = [];
@@ -82,6 +89,7 @@ export struct WebDavMainPage {
   @State visibleFoldersState: FileInfo[] = []; // 改为普通状态变量
   @State visibleFoldersState: FileInfo[] = []; // 改为普通状态变量
   @State isShowFileName: boolean = false//是否显示文件名
   @State isShowFileName: boolean = false//是否显示文件名
   @State isLongNameRoLL: boolean = true//长歌名滚动
   @State isLongNameRoLL: boolean = true//长歌名滚动
+  @State sortType: number = 0 //默认排序方式
 
 
   async onSwitchAccount(){
   async onSwitchAccount(){
     console.log('onecold 切换账户:', this.selectedAccount.name);
     console.log('onecold 切换账户:', this.selectedAccount.name);
@@ -102,8 +110,13 @@ export struct WebDavMainPage {
     this.breadcrumbs = this.webdavManager.getBreadcrumbs();
     this.breadcrumbs = this.webdavManager.getBreadcrumbs();
   }
   }
 
 
-  updateListData(mList:Array<VideoItem>){
-    this.dataSource.pushArrayData(mList)
+  updateListData(mList:Array<VideoItem>, noSort?: boolean){
+    this.songs = mList;
+    if (!noSort) {
+      this.sortType = PreferencesUtil.getNumberSync('webDavSortType', 0)
+      this.doSortType(this.sortType)
+    }
+    this.dataSource.pushArrayData(this.songs)
   }
   }
 
 
   // 更新可见文件夹列表
   // 更新可见文件夹列表
@@ -154,9 +167,12 @@ export struct WebDavMainPage {
     this.handleWebdavEvent(event);
     this.handleWebdavEvent(event);
   };
   };
 
 
+
   aboutToAppear(): void {
   aboutToAppear(): void {
     this.isShowFileName = PreferencesUtil.getBooleanSync('isShowFileName', false)
     this.isShowFileName = PreferencesUtil.getBooleanSync('isShowFileName', false)
     this.isLongNameRoLL = PreferencesUtil.getBooleanSync('isLongNameRoLL', true)
     this.isLongNameRoLL = PreferencesUtil.getBooleanSync('isLongNameRoLL', true)
+    this.sortType = PreferencesUtil.getNumberSync('webDavSortType', 0)
+    this.isNoJumpToHome = PreferencesUtil.getBooleanSync('isNoJumpToHome', true)
     // 获取顶部安全区高度
     // 获取顶部安全区高度
     this.getTopRectHeight();
     this.getTopRectHeight();
 
 
@@ -348,10 +364,13 @@ export struct WebDavMainPage {
 
 
       Logger.info(TAG, `heanup 发送WebDAV播放事件,只传递索引: ${index}`);
       Logger.info(TAG, `heanup 发送WebDAV播放事件,只传递索引: ${index}`);
 
 
-      // 跳转到首页播放器
-      this.getUIContext()?.animateTo({ duration: 555 }, () => {
-        this.mType = 0
-      })
+      if(!this.isNoJumpToHome){
+        // 跳转到首页播放器
+        this.getUIContext()?.animateTo({ duration: 555 }, () => {
+          this.mType = 0
+        })
+      }
+
     } catch (error) {
     } catch (error) {
       const err = error as Error;
       const err = error as Error;
       Logger.error(TAG, '播放歌曲失败: ' + err.message);
       Logger.error(TAG, '播放歌曲失败: ' + err.message);
@@ -499,6 +518,75 @@ export struct WebDavMainPage {
     }
     }
   }
   }
 
 
+  @Builder
+  SortMenuBuilder() {
+    Menu() {
+
+      MenuItem({
+        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.text_and_arrow_up')),
+        content: $r('app.string.sort_by_name')
+      })
+        .onClick(async () => {
+          this.doSortType(0)
+          PreferencesUtil.put("webDavSortType", 0)
+          this.updateListData(this.songs, true)
+        })
+      MenuItem({
+        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.text_and_arrow_down')),
+        content: '按名称降序'
+      })
+        .onClick(async () => {
+          this.doSortType(1)
+          PreferencesUtil.put("webDavSortType", 1)
+          this.updateListData(this.songs, true)
+        })
+      MenuItem({
+        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.clock')),
+        content: $r('app.string.sort_by_time')
+      })
+        .onClick(async () => {
+          this.doSortType(2)
+          PreferencesUtil.put("webDavSortType", 2)
+          this.updateListData(this.songs, true)
+        })
+
+      MenuItem({
+        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.timer')),
+        content: '按添加时间降序'
+      })
+        .onClick(async () => {
+          this.doSortType(3)
+          PreferencesUtil.put("webDavSortType", 3)
+          this.updateListData(this.songs, true)
+        })
+    }.attributeModifier(new MenuModifier())
+  }
+
+
+  doSortType(index: number) {
+    switch (index) {
+      case 0:
+        Utility.doSortListAscending(this.songs,this.isShowFileName)
+        break;
+      case 1:
+        Utility.doSortListDescending(this.songs,this.isShowFileName)
+        break;
+      case 2:
+        this.songs.sort((a, b) => {
+          // If types are the same, sort by cTime in ascending order
+          return a.cTime.localeCompare(b.cTime);
+        });
+        break;
+      case 3:
+        this.songs.sort((a, b) => {
+          // If types are the same, sort by cTime in descending order
+          return b.cTime.localeCompare(a.cTime);
+        });
+        break;
+
+    }
+  }
+
 
 
   @Builder
   @Builder
   topTitleBar(){
   topTitleBar(){
@@ -534,6 +622,50 @@ export struct WebDavMainPage {
           .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
           .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
 
 
 
 
+
+        //搜索按钮
+        if (!this.isSearchMode) {
+          Button({ type: ButtonType.Circle, stateEffect: true }) {
+            SymbolGlyph($r('sys.symbol.magnifyingglass'))
+              .attributeModifier(new SymbolGlyphFancyModifier(25, '', ''))
+          }
+          .attributeModifier(new ButtonFancyModifier(40, 40))
+          .animation({ duration: 300, curve: Curve.Ease })
+          .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
+          .attributeModifier(new ShadowModifier())
+          .zIndex(0)
+          .onClick(()=>{
+            this.isSearchMode = true
+          })
+          //排序按钮
+          Button({ type: ButtonType.Circle, stateEffect: true }) {
+            SymbolGlyph($r('sys.symbol.list_number'))
+              .attributeModifier(new SymbolGlyphFancyModifier(25, '', ''))
+          }
+          .attributeModifier(new ButtonFancyModifier(40, 40))
+          .animation({ duration: 300, curve: Curve.Ease })
+          .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
+          .bindMenu(this.SortMenuBuilder)
+          .attributeModifier(new ShadowModifier())
+          .zIndex(0)
+          //添加按钮
+          Button({ type: ButtonType.Circle, stateEffect: true }) {
+            SymbolGlyph($r('sys.symbol.plus'))
+              .attributeModifier(new SymbolGlyphFancyModifier(25, '', ''))
+          }
+          .attributeModifier(new ButtonFancyModifier(40, 40))
+          .animation({ duration: 300, curve: Curve.Ease })
+          .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
+          .onClick(() => {
+            this.createPlaylistFromCurrentWebDav();
+          })
+          .attributeModifier(new ShadowModifier())
+          .zIndex(0)
+
+
+        }
+
+
       }
       }
     }
     }
     .padding({ top: this.topSafeHeight+10, left: 10, right: 10,bottom:2 })
     .padding({ top: this.topSafeHeight+10, left: 10, right: 10,bottom:2 })
@@ -633,31 +765,31 @@ export struct WebDavMainPage {
         }
         }
 
 
         // 统计信息
         // 统计信息
-      if (this.webDavFiles.length > 0) {
-          Row() {
-            Text(this.visibleFoldersState.length + ' 个文件夹, ' + this.songs.length + ' 首歌曲')
-              .fontSize(13)
-              .fontColor($r('app.color.index_tab_font_color'))
-              .opacity(0.6)
-              .layoutWeight(1)
-              .textAlign(TextAlign.Start)
-            Blank()
-            Button('一键创建歌单',{ type: ButtonType.Capsule, stateEffect: true })
-              .type(ButtonType.Normal)
-              .fontSize(12)
-              .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.8 })
-              .backgroundColor(this.themeColor)
-              .fontColor(Color.White)
-              .padding({ left: 12, right: 12, top: 6, bottom: 6 })
-              .borderRadius(16)
-              .visibility(this.songs.length > 0 ? Visibility.Visible : Visibility.None)
-              .onClick(() => {
-                Logger.info(TAG, 'heanup 点击一键创建歌单按钮');
-                this.createPlaylistFromCurrentWebDav();
-              })
-          }
-          .padding({ left: 4, right: 4 })
-        }
+      // if (this.webDavFiles.length > 0) {
+      //     Row() {
+      //       Text(this.visibleFoldersState.length + ' 个文件夹, ' + this.songs.length + ' 首歌曲')
+      //         .fontSize(13)
+      //         .fontColor($r('app.color.index_tab_font_color'))
+      //         .opacity(0.6)
+      //         .layoutWeight(1)
+      //         .textAlign(TextAlign.Start)
+      //       Blank()
+      //       Button('一键创建歌单',{ type: ButtonType.Capsule, stateEffect: true })
+      //         .type(ButtonType.Normal)
+      //         .fontSize(12)
+      //         .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.8 })
+      //         .backgroundColor(this.themeColor)
+      //         .fontColor(Color.White)
+      //         .padding({ left: 12, right: 12, top: 6, bottom: 6 })
+      //         .borderRadius(16)
+      //         .visibility(this.songs.length > 0 ? Visibility.Visible : Visibility.None)
+      //         .onClick(() => {
+      //           Logger.info(TAG, 'heanup 点击一键创建歌单按钮');
+      //           this.createPlaylistFromCurrentWebDav();
+      //         })
+      //     }
+      //     .padding({ left: 4, right: 4 })
+      //   }
       }
       }
       .width('100%')
       .width('100%')
       .padding(12)
       .padding(12)
@@ -790,37 +922,63 @@ export struct WebDavMainPage {
           Text(this.isShowFileName?song.fileName :song.name)
           Text(this.isShowFileName?song.fileName :song.name)
             .fontSize(15)
             .fontSize(15)
             .textOverflow({ overflow: this.isLongNameRoLL?TextOverflow.MARQUEE:TextOverflow.Ellipsis })//超长滚动
             .textOverflow({ overflow: this.isLongNameRoLL?TextOverflow.MARQUEE:TextOverflow.Ellipsis })//超长滚动
-            .fontColor($r('app.color.index_tab_font_color'))
+            .fontColor(this.currentSong?.filePath==song.filePath?this.themeColor:$r('app.color.index_tab_font_color'))
             .maxLines(1)
             .maxLines(1)
+            .margin({ right: 20 })
             .textOverflow({ overflow: TextOverflow.Ellipsis })
             .textOverflow({ overflow: TextOverflow.Ellipsis })
 
 
           Row(){
           Row(){
             Text(song.artist+"  ")
             Text(song.artist+"  ")
               .fontSize(13)
               .fontSize(13)
-              .fontColor($r('app.color.index_tab_font_color'))
+              .fontColor(this.currentSong?.filePath==song.filePath?this.themeColor:$r('app.color.index_tab_font_color'))
               .opacity(0.6)
               .opacity(0.6)
               .maxLines(1)
               .maxLines(1)
               .visibility(song.artist?Visibility.Visible:Visibility.None)
               .visibility(song.artist?Visibility.Visible:Visibility.None)
               .textOverflow({ overflow: TextOverflow.Ellipsis })
               .textOverflow({ overflow: TextOverflow.Ellipsis })
             Text(decodeUrlEncodedString(song.size||"")+'  '+song.cTime)
             Text(decodeUrlEncodedString(song.size||"")+'  '+song.cTime)
               .fontSize(13)
               .fontSize(13)
-              .fontColor($r('app.color.index_tab_font_color'))
+              .fontColor(this.currentSong?.filePath==song.filePath?this.themeColor:
+                $r('app.color.index_tab_font_color'))
               .opacity(0.6)
               .opacity(0.6)
               .maxLines(1)
               .maxLines(1)
               .textOverflow({ overflow: TextOverflow.Ellipsis })
               .textOverflow({ overflow: TextOverflow.Ellipsis })
           }
           }
-
+          .margin({ right: 20 })
 
 
         }
         }
         .alignItems(HorizontalAlign.Start)
         .alignItems(HorizontalAlign.Start)
-        .layoutWeight(1)
 
 
-        // 播放图标
-        Image($r('app.media.ic_play'))
-          .width(20)
-          .height(20)
-          .fillColor($r('app.color.index_tab_font_color'))
-          .opacity(0.4)
+
+        Column() {
+          //多选按钮的Checkbox 先注释掉
+          // Checkbox({ name: 'checkbox' + index })
+          //   .select(this.selectedFiles.some(x => x.filePath === item.filePath))
+          //   .selectedColor(this.themeColor)
+          //   .shape(CheckBoxShape.CIRCLE)
+          //   .opacity(this.isMultiSelect ? 1 : 0)
+          //   .animation({
+          //     duration: 666,
+          //     curve: 'Smooth' // 可选动画曲线
+          //   })
+          //   .visibility(this.isMultiSelect ? Visibility.Visible : Visibility.None)
+          //   .onChange((checked: boolean) => this.handleFileSelection(item, checked))
+          //   .margin({ left: 20, top: 8, bottom: 8,right:18 })
+          //   .width(22)
+          //   .height(22)
+
+
+          ImageAnimator()
+            .images(CommonConstants.IMAGE_FRAME_INFO)// 动画数组
+            .duration(1000)// 持续
+            .state(this.animationState)// 动画状态
+            .fillMode(FillMode.Forwards)
+            .width(18)
+            .margin({ right: 12, top: 8, bottom: 8 })
+            .visibility(this.currentSong?.filePath==song.filePath ?  Visibility.Visible :
+              Visibility.None)
+            .height(18)
+            .iterations(-1) // 播放次数
+        }
       }
       }
     }
     }
     .width('100%')
     .width('100%')

+ 6 - 8
entry/src/main/ets/view/LocalMusic.ets

@@ -773,13 +773,11 @@ export struct LocalMusic {
   @State isClickedPLayAll: boolean = false;
   @State isClickedPLayAll: boolean = false;
   private scrollerForlist: ListScroller = new ListScroller();
   private scrollerForlist: ListScroller = new ListScroller();
   private knockController: KnockController | undefined = undefined;
   private knockController: KnockController | undefined = undefined;
-  imagesF: ImageFrameInfo[] = [
-    { src: $r("app.media.app_loading0") },
-    { src: $r("app.media.app_loading1") },
-    { src: $r("app.media.app_loading2") },
-    { src: $r("app.media.app_loading3") },
-  ]
-  @State animationState: AnimationStatus = AnimationStatus.Initial
+
+  // 修改为:
+  imagesF: ImageFrameInfo[] = CommonConstants.IMAGE_FRAME_INFO;
+
+  @StorageProp('animationState') animationState: AnimationStatus = AnimationStatus.Initial
   // 缓存机制
   // 缓存机制
   private cache: Map<string, Array<VideoItem>> = new Map();
   private cache: Map<string, Array<VideoItem>> = new Map();
   private stringCache: Map<string, string> = new Map(); // 新增字符串缓存
   private stringCache: Map<string, string> = new Map(); // 新增字符串缓存
@@ -9583,7 +9581,7 @@ export struct LocalMusic {
       this.setIsPlaying(false)
       this.setIsPlaying(false)
       this.animationState = AnimationStatus.Paused
       this.animationState = AnimationStatus.Paused
     }
     }
-
+    AppStorage.setOrCreate('animationState', this.animationState);
 
 
   }
   }