chendeben 9 месяцев назад
Родитель
Сommit
cc6d9b3f59

+ 1 - 4
entry/src/main/ets/common/util/ReqPermissionUtil.ets

@@ -25,9 +25,6 @@ class ReqPermission {
   async persistPermission(uri: string): Promise<boolean> {
     try {
       if (canIUse('SystemCapability.FileManagement.AppFileService.FolderAuthorization')) {
-        if(uri.startsWith('file://media/Photo')){
-          uri = new fileUri.FileUri(uri).path
-        }
         console.info('onecold 持久化权限persistPermission uri : ', uri);
         let policyInfo: fileShare.PolicyInfo = {
           uri: uri,
@@ -49,4 +46,4 @@ class ReqPermission {
   }
 }
 
-export default new ReqPermission()
+export default new ReqPermission()

+ 126 - 34
entry/src/main/ets/pages/UploadMusicPage.ets

@@ -9,10 +9,11 @@ import { CommonConstants } from '../common/constants/CommonConstants';
 import { Constants } from '../Constants';
 import { picker, fileUri } from '@kit.CoreFileKit';
 import { BusinessError } from '@kit.BasicServicesKit';
-import { PreferencesUtil, AppUtil } from '@pura/harmony-utils';
+import { PreferencesUtil } from '@pura/harmony-utils';
 import { UploadTaskDataSource } from '../viewmodel/UploadTask';
 import { promptAction } from '@kit.ArkUI';
 import fs from '@ohos.file.fs';
+import ReqPermissionUtil from '../common/util/ReqPermissionUtil';
 
 const TAG = 'heanup UploadMusicPage';
 
@@ -64,6 +65,8 @@ export struct UploadMusicPage {
   @State currentBrowsePath: string = '/';
   @State browseFolders: FileInfo[] = [];
   @State isLoadingFolders: boolean = false;
+  @State pendingCount: number = 0;
+  @State finishedCount: number = 0;
 
   // RemoteDriveManager实例
   private webdavManager: RemoteDriveManager = RemoteDriveManager.getInstance();
@@ -79,6 +82,7 @@ export struct UploadMusicPage {
     this.handleUploadEvent(event);
   };
   private cachedDownloadRoot: string = '';
+  private localBrowseRoot: string = '';
 
   aboutToAppear(): void {
     Logger.info(TAG, '页面即将显示');
@@ -132,7 +136,7 @@ export struct UploadMusicPage {
       // 如果没有选中账户且有可用账户,默认选择第一个
       if (!this.selectedAccount && this.accounts.length > 0) {
         this.selectedAccount = this.accounts[0];
-        this.uploadPath = this.selectedAccount.uploadFilePath || '/';
+        this.applyDefaultUploadPath(this.selectedAccount);
       }
     } catch (error) {
       const err = error as Error;
@@ -156,9 +160,7 @@ export struct UploadMusicPage {
       const err = error as Error;
       Logger.warn(TAG, `读取download_path失败: ${err.message}`);
     }
-    const packName = AppUtil.getBundleName();
-    this.cachedDownloadRoot = `/storage/Users/currentUser/Download/${packName}`;
-    return this.cachedDownloadRoot;
+    return '';
   }
 
   private normalizeLocalDirectory(path: string): string {
@@ -189,7 +191,7 @@ export struct UploadMusicPage {
     }
   }
 
-  private async requestDownloadRootFromSystem(): Promise<string> {
+  private async requestDownloadRootFromSystem(force: boolean = false): Promise<string> {
     try {
       const documentViewPicker = new picker.DocumentViewPicker();
       const documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD });
@@ -200,6 +202,7 @@ export struct UploadMusicPage {
           this.cachedDownloadRoot = normalized;
           const storedValue = normalized.endsWith('/') ? normalized : `${normalized}/`;
           PreferencesUtil.putSync('download_path', storedValue);
+          await ReqPermissionUtil.persistPermission(documentSaveResult[0]);
           return normalized;
         }
       }
@@ -219,7 +222,11 @@ export struct UploadMusicPage {
     if (requested && this.ensureDirectoryAccessible(requested)) {
       return requested;
     }
-    return '';
+    promptAction.showToast({
+      message: '请先在本地音乐页授权下载目录后再使用此功能',
+      duration: 2000
+    });
+    throw new Error('未授权任何本地目录');
   }
 
   private normalizeBrowsePath(path: string): string {
@@ -247,7 +254,7 @@ export struct UploadMusicPage {
         const account = this.accounts[i];
         if (account.id === accountId) {
           this.selectedAccount = account;
-          this.uploadPath = account.uploadFilePath || '/';
+          this.applyDefaultUploadPath(account);
           Logger.info(TAG, `预选账户: ${account.name}`);
           break;
         }
@@ -390,10 +397,18 @@ export struct UploadMusicPage {
       this.uploadQueue.push(tasks[i].song);
     }
     
-    // 更新LazyDataSource
+    this.pendingCount = this.uploadQueue.length;
+    
+    if (this.pendingCount === 0) {
+      this.isUploading = false;
+      this.currentTask = null;
+      this.uploadProgress = 0;
+      this.uploadSpeed = '0 KB/s';
+    }
+    
     this.uploadQueueDataSource.updateTasks(this.uploadQueue);
     
-    Logger.info(TAG, `上传队列更新,当前数量: ${this.uploadQueue.length}`);
+    Logger.info(TAG, `上传队列更新,当前数量: ${this.pendingCount}`);
   }
 
   /**
@@ -406,10 +421,11 @@ export struct UploadMusicPage {
       this.finishQueue.push(tasks[i].song);
     }
     
-    // 更新LazyDataSource
+    this.finishedCount = this.finishQueue.length;
+    
     this.finishQueueDataSource.updateTasks(this.finishQueue);
     
-    Logger.info(TAG, `完成队列更新,当前数量: ${this.finishQueue.length}`);
+    Logger.info(TAG, `完成队列更新,当前数量: ${this.finishedCount}`);
   }
 
   /**
@@ -430,6 +446,14 @@ export struct UploadMusicPage {
       const documentSelectResult = await documentPicker.select(documentSelectOptions);
       
       if (documentSelectResult && documentSelectResult.length > 0) {
+        for (let i = 0; i < documentSelectResult.length; i++) {
+          try {
+            await ReqPermissionUtil.persistPermission(documentSelectResult[i]);
+          } catch (error) {
+            const err = error as Error;
+            Logger.error(TAG, `持久化授权失败: ${err.message}`);
+          }
+        }
         Logger.info(TAG, `选择了 ${documentSelectResult.length} 个文件`);
         
         // 保存选中的文件URI
@@ -476,15 +500,16 @@ export struct UploadMusicPage {
       }
 
       this.fileBrowserPath = initialPath;
+      this.localBrowseRoot = initialPath;
       this.selectedBrowserItems.clear();
       this.isShowFileBrowser = true;
       await this.loadLocalFiles(initialPath);
       Logger.info(TAG, `文件浏览器已定位到: ${initialPath}`);
     } catch (error) {
-      const err = error as BusinessError;
+      const err = error as Error;
       Logger.error(TAG, `打开文件浏览器失败: ${err.message}`);
       promptAction.showToast({
-        message: '打开文件浏览器失败',
+        message: err.message || '打开文件浏览器失败',
         duration: 2000
       });
     }
@@ -497,14 +522,17 @@ export struct UploadMusicPage {
     try {
       this.isLoadingFiles = true;
       this.browserFiles = [];
+      let targetPath = path;
+      if (this.localBrowseRoot && !targetPath.startsWith(this.localBrowseRoot)) {
+        targetPath = this.localBrowseRoot;
+      }
+      Logger.info(TAG, `加载目录: ${targetPath}`);
       
-      Logger.info(TAG, `加载目录: ${path}`);
-      
-      const files = fs.listFileSync(path);
+      const files = fs.listFileSync(targetPath);
       
       for (let i = 0; i < files.length; i++) {
         const fileName = files[i];
-        const fullPath = `${path}/${fileName}`;
+        const fullPath = `${targetPath}/${fileName}`;
         
         try {
           const stat = fs.statSync(fullPath);
@@ -553,19 +581,39 @@ export struct UploadMusicPage {
    * 进入子目录
    */
   private async enterDirectory(item: LocalFileItem): Promise<void> {
-    if (item.isDirectory) {
-      this.fileBrowserPath = item.path;
-      await this.loadLocalFiles(item.path);
+    if (!item.isDirectory) {
+      return;
+    }
+    if (this.localBrowseRoot && !item.path.startsWith(this.localBrowseRoot)) {
+      promptAction.showToast({
+        message: '没有此目录的访问权限',
+        duration: 2000
+      });
+      return;
     }
+    this.fileBrowserPath = item.path;
+    await this.loadLocalFiles(item.path);
   }
 
   /**
    * 返回上级目录
    */
   private async goBackDirectory(): Promise<void> {
+    if (!this.localBrowseRoot) {
+      return;
+    }
+    if (this.fileBrowserPath === this.localBrowseRoot) {
+      return;
+    }
     const lastSlash = this.fileBrowserPath.lastIndexOf('/');
     if (lastSlash > 0) {
-      this.fileBrowserPath = this.fileBrowserPath.substring(0, lastSlash);
+      const parent = this.fileBrowserPath.substring(0, lastSlash);
+      if (!parent.startsWith(this.localBrowseRoot)) {
+        this.fileBrowserPath = this.localBrowseRoot;
+        await this.loadLocalFiles(this.localBrowseRoot);
+        return;
+      }
+      this.fileBrowserPath = parent;
       await this.loadLocalFiles(this.fileBrowserPath);
     }
   }
@@ -586,7 +634,7 @@ export struct UploadMusicPage {
   /**
    * 确认选择文件
    */
-  private confirmFileSelection(): void {
+  private async confirmFileSelection(): Promise<void> {
     if (this.selectedBrowserItems.size === 0) {
       promptAction.showToast({
         message: '请至少选择一个项目',
@@ -596,16 +644,28 @@ export struct UploadMusicPage {
     }
 
     // 将选中的项目添加到已选列表
-    this.selectedBrowserItems.forEach(path => {
-      const item = this.browserFiles.find(f => f.path === path);
-      if (item) {
-        const uri = `file://${path}`;
-        this.selectedFiles.push(uri);
-        this.selectedFileNames.push(item.name);
-        this.selectedFileTypes.push(item.isDirectory ? 'folder' : 'file');
+    const selectedItems: LocalFileItem[] = [];
+    this.selectedBrowserItems.forEach((path) => {
+      const found = this.browserFiles.find((f) => f.path === path);
+      if (found) {
+        selectedItems.push(found);
       }
     });
 
+    for (let i = 0; i < selectedItems.length; i++) {
+      const item = selectedItems[i];
+      const uri = `file://${item.path}`;
+      try {
+        await ReqPermissionUtil.persistPermission(uri);
+      } catch (error) {
+        const err = error as Error;
+        Logger.error(TAG, `持久化授权失败: ${err.message}`);
+      }
+      this.selectedFiles.push(uri);
+      this.selectedFileNames.push(item.name);
+      this.selectedFileTypes.push(item.isDirectory ? 'folder' : 'file');
+    }
+
     promptAction.showToast({
       message: `已添加 ${this.selectedBrowserItems.size} 个项目`,
       duration: 2000
@@ -621,6 +681,7 @@ export struct UploadMusicPage {
     this.isShowFileBrowser = false;
     this.selectedBrowserItems.clear();
     this.browserFiles = [];
+    this.localBrowseRoot = '';
   }
 
   /**
@@ -695,10 +756,28 @@ export struct UploadMusicPage {
    */
   private selectUploadAccount(account: WebDavAccount): void {
     this.selectedAccount = account;
-    this.uploadPath = account.uploadFilePath || '/';
+    this.applyDefaultUploadPath(account);
     Logger.info(TAG, `选择账户: ${account.name}, 上传路径: ${this.uploadPath}`);
   }
 
+  private applyDefaultUploadPath(account: WebDavAccount | null): void {
+    if (!account) {
+      return;
+    }
+    const managerAccount = this.webdavManager.currentAccount;
+    const managerPath = this.webdavManager.currentPath;
+    if (
+      managerPath &&
+      managerPath.length > 0 &&
+      managerAccount &&
+      managerAccount.id === account.id
+    ) {
+      this.uploadPath = managerPath;
+    } else {
+      this.uploadPath = account.uploadFilePath || '/';
+    }
+  }
+
   /**
    * 打开路径浏览器
    */
@@ -711,8 +790,17 @@ export struct UploadMusicPage {
     Logger.info(TAG, '========== 打开路径浏览器 ==========');
     Logger.info(TAG, `当前上传路径: ${this.uploadPath}`);
     Logger.info(TAG, `选中账户: ${this.selectedAccount.name}`);
+
+    const managerAccount = this.webdavManager.currentAccount;
+    const defaultPath =
+      managerAccount &&
+        managerAccount.id === this.selectedAccount.id &&
+        this.webdavManager.currentPath &&
+        this.webdavManager.currentPath.length > 0
+        ? this.webdavManager.currentPath
+        : this.uploadPath;
     
-    this.currentBrowsePath = this.uploadPath;
+    this.currentBrowsePath = defaultPath;
     this.isShowPathBrowser = true;
     
     Logger.info(TAG, `isShowPathBrowser 设置为: ${this.isShowPathBrowser}`);
@@ -933,6 +1021,9 @@ export struct UploadMusicPage {
       await this.webdavManager.startUploadQueue();
 
       Logger.info(TAG, '上传任务已启动');
+      this.selectedFiles = [];
+      this.selectedFileNames = [];
+      this.selectedFileTypes = [];
     } catch (error) {
       const err = error as Error;
       Logger.error(TAG, `启动上传失败: ${err.message}`);
@@ -1119,12 +1210,12 @@ export struct UploadMusicPage {
         TabContent() {
           this.QueueList(this.uploadQueueDataSource, 'pending')
         }
-        .tabBar(this.TabBarBuilder('⏳', '待上传', this.uploadQueue.length))
+        .tabBar(this.TabBarBuilder('⏳', '待上传', this.pendingCount))
 
         TabContent() {
           this.QueueList(this.finishQueueDataSource, 'finished')
         }
-        .tabBar(this.TabBarBuilder('✅', '已完成', this.finishQueue.length))
+        .tabBar(this.TabBarBuilder('✅', '已完成', this.finishedCount))
       }
       .width('100%')
       .height(300)
@@ -1835,6 +1926,7 @@ export struct UploadMusicPage {
         .onClick(() => {
           this.openFileBrowser();
         })
+
       }
       .width('100%')
 

+ 20 - 16
entry/src/main/ets/pages/WebDavMainPage.ets

@@ -13,6 +13,7 @@ import { emitter } from '@kit.BasicServicesKit';
 import { EventConstants } from '../common/constants/EventConstants';
 import { LazyDataSource } from '../common/util/LazyDataSource';
 import { PreferencesUtil, StrUtil } from '@pura/harmony-utils';
+import { DialogHelper } from '@pura/harmony-dialog';
 import { ButtonFancyModifier,
   MenuModifier,
   ShadowModifier, SymbolGlyphFancyModifier } from '../common/util/AttributeModifierUtil';
@@ -692,6 +693,23 @@ export struct WebDavMainPage {
     });
   }
 
+  private showQuickActionSheet(): void {
+    DialogHelper.showActionSheetDialog({
+      title: '请选择操作',
+      sheets: [
+        { value: '一键创建歌单', fontColor: $r('app.color.text_color') },
+        { value: '上传到WebDAV', fontColor: $r('app.color.text_color') }
+      ],
+      onAction: (index: number) => {
+        if (index === 0) {
+          this.createPlaylistFromCurrentWebDav();
+        } else if (index === 1) {
+          this.navigateToUploadPage();
+        }
+      }
+    });
+  }
+
   @Builder
   SortMenuBuilder() {
     Menu() {
@@ -911,7 +929,7 @@ export struct WebDavMainPage {
           .bindMenu(this.SortMenuBuilder)
           .attributeModifier(new ShadowModifier())
           .zIndex(0)
-          //添加按钮
+          //添加/上传综合按钮
           Button({ type: ButtonType.Circle, stateEffect: true }) {
             SymbolGlyph($r('sys.symbol.plus'))
               .attributeModifier(new SymbolGlyphFancyModifier(25, '', ''))
@@ -920,21 +938,7 @@ export struct WebDavMainPage {
           .animation({ duration: 300, curve: Curve.Ease })
           .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
           .onClick(() => {
-            this.createPlaylistFromCurrentWebDav();
-          })
-          .attributeModifier(new ShadowModifier())
-          .zIndex(0)
-          
-          //上传按钮
-          Button({ type: ButtonType.Circle, stateEffect: true }) {
-            SymbolGlyph($r('sys.symbol.arrow_up'))
-              .attributeModifier(new SymbolGlyphFancyModifier(25, '', ''))
-          }
-          .attributeModifier(new ButtonFancyModifier(40, 40))
-          .animation({ duration: 300, curve: Curve.Ease })
-          .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
-          .onClick(() => {
-            this.navigateToUploadPage();
+            this.showQuickActionSheet();
           })
           .attributeModifier(new ShadowModifier())
           .zIndex(0)