|
@@ -0,0 +1,1382 @@
|
|
|
|
|
+import { RemoteDriveManager } from '../common/util/RemoteDriveManager';
|
|
|
|
|
+import { WebDavAccount } from '../viewmodel/WebDavAccount';
|
|
|
|
|
+import { VideoItem } from '../viewmodel/VideoItem';
|
|
|
|
|
+import { FileInfo } from '../viewmodel/FileInfo';
|
|
|
|
|
+import { RemoteDriveManagerStates } from '../common/enums/RemoteDriveManagerStates';
|
|
|
|
|
+import Logger from '../common/util/Logger';
|
|
|
|
|
+import { router } from '@kit.ArkUI';
|
|
|
|
|
+import { CommonConstants } from '../common/constants/CommonConstants';
|
|
|
|
|
+import { picker } from '@kit.CoreFileKit';
|
|
|
|
|
+import { BusinessError } from '@kit.BasicServicesKit';
|
|
|
|
|
+import { fileUri } from '@kit.CoreFileKit';
|
|
|
|
|
+import { PreferencesUtil } from '@pura/harmony-utils';
|
|
|
|
|
+import { UploadTaskDataSource } from '../viewmodel/UploadTask';
|
|
|
|
|
+
|
|
|
|
|
+const TAG = 'UploadMusicPage';
|
|
|
|
|
+
|
|
|
|
|
+@Entry
|
|
|
|
|
+@Component
|
|
|
|
|
+export struct UploadMusicPage {
|
|
|
|
|
+ @StorageProp('topSafeHeight') topSafeHeight: number = 0;
|
|
|
|
|
+ @StorageProp('bottomSafeHeight') bottomSafeHeight: number = 0;
|
|
|
|
|
+ @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
|
|
|
|
|
+ @StorageProp('isDarkMode') isDarkMode: boolean = false;
|
|
|
|
|
+
|
|
|
|
|
+ // 页面状态变量
|
|
|
|
|
+ @State selectedFiles: string[] = [];
|
|
|
|
|
+ @State selectedFileNames: string[] = [];
|
|
|
|
|
+ @State accounts: WebDavAccount[] = [];
|
|
|
|
|
+ @State selectedAccount: WebDavAccount | null = null;
|
|
|
|
|
+ @State uploadPath: string = '/';
|
|
|
|
|
+ @State availablePaths: string[] = [];
|
|
|
|
|
+ @State duplicateAction: string = 'skip';
|
|
|
|
|
+ @State uploadProgress: number = 0;
|
|
|
|
|
+ @State uploadSpeed: string = '0 KB/s';
|
|
|
|
|
+ @State uploadedCount: number = 0;
|
|
|
|
|
+ @State totalUploadCount: number = 0;
|
|
|
|
|
+ @State isUploading: boolean = false;
|
|
|
|
|
+ @State uploadQueue: VideoItem[] = [];
|
|
|
|
|
+ @State finishQueue: VideoItem[] = [];
|
|
|
|
|
+ @State currentTask: VideoItem | null = null;
|
|
|
|
|
+ @State isShowPathBrowser: boolean = false;
|
|
|
|
|
+ @State currentBrowsePath: string = '/';
|
|
|
|
|
+ @State browseFolders: FileInfo[] = [];
|
|
|
|
|
+ @State isLoadingFolders: boolean = false;
|
|
|
|
|
+
|
|
|
|
|
+ // RemoteDriveManager实例
|
|
|
|
|
+ private webdavManager: RemoteDriveManager = RemoteDriveManager.getInstance();
|
|
|
|
|
+
|
|
|
|
|
+ // 性能优化:使用LazyDataSource优化队列列表渲染
|
|
|
|
|
+ // LazyForEach配合IDataSource可以实现按需加载,避免一次性渲染大量列表项
|
|
|
|
|
+ // 对于包含数百个上传任务的队列,可以显著提升UI响应性能
|
|
|
|
|
+ private uploadQueueDataSource: UploadTaskDataSource = new UploadTaskDataSource();
|
|
|
|
|
+ private finishQueueDataSource: UploadTaskDataSource = new UploadTaskDataSource();
|
|
|
|
|
+
|
|
|
|
|
+ // 事件处理器引用
|
|
|
|
|
+ private eventHandler: (event: string) => void = (event: string) => {
|
|
|
|
|
+ this.handleUploadEvent(event);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ aboutToAppear(): void {
|
|
|
|
|
+ Logger.info(TAG, '页面即将显示');
|
|
|
|
|
+
|
|
|
|
|
+ // 加载用户设置
|
|
|
|
|
+ this.loadUserSettings();
|
|
|
|
|
+
|
|
|
|
|
+ // 加载WebDAV账户列表
|
|
|
|
|
+ this.loadAccounts();
|
|
|
|
|
+
|
|
|
|
|
+ // 从路由参数获取预选账户ID
|
|
|
|
|
+ const params = router.getParams() as Record<string, Object>;
|
|
|
|
|
+ if (params && params['accountId']) {
|
|
|
|
|
+ const accountId = params['accountId'] as number;
|
|
|
|
|
+ this.preselectAccount(accountId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 订阅RemoteDriveManager的上传事件
|
|
|
|
|
+ this.subscribeUploadEvents();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 加载用户设置
|
|
|
|
|
+ */
|
|
|
|
|
+ private loadUserSettings(): void {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 加载默认重复文件处理方式
|
|
|
|
|
+ this.duplicateAction = PreferencesUtil.getStringSync('webdavUploadDuplicateAction', 'skip');
|
|
|
|
|
+ Logger.info(TAG, `加载用户设置 - 重复文件处理: ${this.duplicateAction}`);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as Error;
|
|
|
|
|
+ Logger.error(TAG, `加载用户设置失败: ${err.message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ aboutToDisappear(): void {
|
|
|
|
|
+ Logger.info(TAG, '页面即将销毁');
|
|
|
|
|
+
|
|
|
|
|
+ // 取消订阅事件
|
|
|
|
|
+ this.unsubscribeUploadEvents();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 加载WebDAV账户列表
|
|
|
|
|
+ */
|
|
|
|
|
+ private loadAccounts(): void {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.accounts = this.webdavManager.getAllWebDavAccounts();
|
|
|
|
|
+ Logger.info(TAG, `加载了 ${this.accounts.length} 个WebDAV账户`);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有选中账户且有可用账户,默认选择第一个
|
|
|
|
|
+ if (!this.selectedAccount && this.accounts.length > 0) {
|
|
|
|
|
+ this.selectedAccount = this.accounts[0];
|
|
|
|
|
+ this.uploadPath = this.selectedAccount.uploadFilePath || '/';
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as Error;
|
|
|
|
|
+ Logger.error(TAG, `加载账户列表失败: ${err.message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 预选指定账户
|
|
|
|
|
+ * @param accountId 账户ID
|
|
|
|
|
+ */
|
|
|
|
|
+ private preselectAccount(accountId: number): void {
|
|
|
|
|
+ try {
|
|
|
|
|
+ for (let i = 0; i < this.accounts.length; i++) {
|
|
|
|
|
+ const account = this.accounts[i];
|
|
|
|
|
+ if (account.id === accountId) {
|
|
|
|
|
+ this.selectedAccount = account;
|
|
|
|
|
+ this.uploadPath = account.uploadFilePath || '/';
|
|
|
|
|
+ Logger.info(TAG, `预选账户: ${account.name}`);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as Error;
|
|
|
|
|
+ Logger.error(TAG, `预选账户失败: ${err.message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 订阅RemoteDriveManager的上传事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private subscribeUploadEvents(): void {
|
|
|
|
|
+ this.webdavManager.subscribe(this.eventHandler);
|
|
|
|
|
+ Logger.info(TAG, '已订阅上传事件');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 取消订阅RemoteDriveManager的上传事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private unsubscribeUploadEvents(): void {
|
|
|
|
|
+ this.webdavManager.unsubscribe(this.eventHandler);
|
|
|
|
|
+ Logger.info(TAG, '已取消订阅上传事件');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理上传事件
|
|
|
|
|
+ * @param event 事件类型
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleUploadEvent(event: string): void {
|
|
|
|
|
+ Logger.info(TAG, `收到上传事件: ${event}`);
|
|
|
|
|
+
|
|
|
|
|
+ switch (event) {
|
|
|
|
|
+ case RemoteDriveManagerStates.UploadStart:
|
|
|
|
|
+ this.handleUploadStart();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case RemoteDriveManagerStates.UploadProgress:
|
|
|
|
|
+ this.handleUploadProgress();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case RemoteDriveManagerStates.UploadSuccess:
|
|
|
|
|
+ this.handleUploadSuccess();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case RemoteDriveManagerStates.UploadFailed:
|
|
|
|
|
+ this.handleUploadFailed();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case RemoteDriveManagerStates.UploadPaused:
|
|
|
|
|
+ this.handleUploadPaused();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case RemoteDriveManagerStates.UploadResumed:
|
|
|
|
|
+ this.handleUploadResumed();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case RemoteDriveManagerStates.SetCurrentUploadTask:
|
|
|
|
|
+ this.handleCurrentTaskChanged();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case RemoteDriveManagerStates.ChangeUploadQueue:
|
|
|
|
|
+ this.handleUploadQueueChanged();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case RemoteDriveManagerStates.ChangeFinishUploadQueue:
|
|
|
|
|
+ this.handleFinishQueueChanged();
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理上传开始事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleUploadStart(): void {
|
|
|
|
|
+ this.isUploading = true;
|
|
|
|
|
+ Logger.info(TAG, '上传开始');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理上传进度更新事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleUploadProgress(): void {
|
|
|
|
|
+ const uploaded = this.webdavManager.uploadReceivedSize;
|
|
|
|
|
+ const total = this.webdavManager.uploadTotalSize;
|
|
|
|
|
+
|
|
|
|
|
+ if (total > 0) {
|
|
|
|
|
+ this.uploadProgress = Math.floor((uploaded / total) * 100);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算上传速度(简化版,实际应该基于时间差计算)
|
|
|
|
|
+ const speedMBps = uploaded / (1024 * 1024);
|
|
|
|
|
+ this.uploadSpeed = `${speedMBps.toFixed(2)} MB/s`;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Logger.info(TAG, `上传进度: ${this.uploadProgress}%`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理上传成功事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleUploadSuccess(): void {
|
|
|
|
|
+ this.uploadedCount++;
|
|
|
|
|
+ Logger.info(TAG, `上传成功,已完成: ${this.uploadedCount}/${this.totalUploadCount}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理上传失败事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleUploadFailed(): void {
|
|
|
|
|
+ Logger.error(TAG, '上传失败');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理上传暂停事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleUploadPaused(): void {
|
|
|
|
|
+ Logger.info(TAG, '上传已暂停');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理上传恢复事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleUploadResumed(): void {
|
|
|
|
|
+ Logger.info(TAG, '上传已恢复');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理当前任务变更事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleCurrentTaskChanged(): void {
|
|
|
|
|
+ const task = this.webdavManager.currentUploadTask;
|
|
|
|
|
+ if (task) {
|
|
|
|
|
+ this.currentTask = task.song;
|
|
|
|
|
+ Logger.info(TAG, `当前上传任务: ${task.song.name}`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.currentTask = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理上传队列变更事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleUploadQueueChanged(): void {
|
|
|
|
|
+ const tasks = this.webdavManager.uploadQueue;
|
|
|
|
|
+ this.uploadQueue = [];
|
|
|
|
|
+ for (let i = 0; i < tasks.length; i++) {
|
|
|
|
|
+ this.uploadQueue.push(tasks[i].song);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新LazyDataSource
|
|
|
|
|
+ this.uploadQueueDataSource.updateTasks(this.uploadQueue);
|
|
|
|
|
+
|
|
|
|
|
+ Logger.info(TAG, `上传队列更新,当前数量: ${this.uploadQueue.length}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 处理完成队列变更事件
|
|
|
|
|
+ */
|
|
|
|
|
+ private handleFinishQueueChanged(): void {
|
|
|
|
|
+ const tasks = this.webdavManager.finishUploadQueue;
|
|
|
|
|
+ this.finishQueue = [];
|
|
|
|
|
+ for (let i = 0; i < tasks.length; i++) {
|
|
|
|
|
+ this.finishQueue.push(tasks[i].song);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新LazyDataSource
|
|
|
|
|
+ this.finishQueueDataSource.updateTasks(this.finishQueue);
|
|
|
|
|
+
|
|
|
|
|
+ Logger.info(TAG, `完成队列更新,当前数量: ${this.finishQueue.length}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 打开文件选择器
|
|
|
|
|
+ */
|
|
|
|
|
+ private async openFilePicker(): Promise<void> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const documentSelectOptions = new picker.DocumentSelectOptions();
|
|
|
|
|
+
|
|
|
|
|
+ // 设置音频文件过滤器
|
|
|
|
|
+ documentSelectOptions.fileSuffixFilters = CommonConstants.AUDIO_EXTENSIONS;
|
|
|
|
|
+
|
|
|
|
|
+ // 支持多选
|
|
|
|
|
+ documentSelectOptions.maxSelectNumber = 100;
|
|
|
|
|
+
|
|
|
|
|
+ const documentPicker = new picker.DocumentViewPicker();
|
|
|
|
|
+
|
|
|
|
|
+ const documentSelectResult = await documentPicker.select(documentSelectOptions);
|
|
|
|
|
+
|
|
|
|
|
+ if (documentSelectResult && documentSelectResult.length > 0) {
|
|
|
|
|
+ Logger.info(TAG, `选择了 ${documentSelectResult.length} 个文件`);
|
|
|
|
|
+
|
|
|
|
|
+ // 保存选中的文件URI
|
|
|
|
|
+ this.selectedFiles = documentSelectResult;
|
|
|
|
|
+
|
|
|
|
|
+ // 提取文件名用于显示
|
|
|
|
|
+ this.selectedFileNames = [];
|
|
|
|
|
+ for (let i = 0; i < documentSelectResult.length; i++) {
|
|
|
|
|
+ const uri = documentSelectResult[i];
|
|
|
|
|
+ try {
|
|
|
|
|
+ const fileUriObj = new fileUri.FileUri(uri);
|
|
|
|
|
+ const fileName = fileUriObj.name;
|
|
|
|
|
+ this.selectedFileNames.push(fileName);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as Error;
|
|
|
|
|
+ Logger.error(TAG, `解析文件URI失败: ${err.message}`);
|
|
|
|
|
+ this.selectedFileNames.push('未知文件');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Logger.info(TAG, `文件列表: ${JSON.stringify(this.selectedFileNames)}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as BusinessError;
|
|
|
|
|
+ Logger.error(TAG, `文件选择失败: ${err.message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 移除已选文件
|
|
|
|
|
+ * @param index 文件索引
|
|
|
|
|
+ */
|
|
|
|
|
+ private removeSelectedFile(index: number): void {
|
|
|
|
|
+ if (index >= 0 && index < this.selectedFiles.length) {
|
|
|
|
|
+ this.selectedFiles.splice(index, 1);
|
|
|
|
|
+ this.selectedFileNames.splice(index, 1);
|
|
|
|
|
+ Logger.info(TAG, `移除文件,剩余 ${this.selectedFiles.length} 个`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 选择上传账户
|
|
|
|
|
+ * @param account 选中的账户
|
|
|
|
|
+ */
|
|
|
|
|
+ private selectUploadAccount(account: WebDavAccount): void {
|
|
|
|
|
+ this.selectedAccount = account;
|
|
|
|
|
+ this.uploadPath = account.uploadFilePath || '/';
|
|
|
|
|
+ Logger.info(TAG, `选择账户: ${account.name}, 上传路径: ${this.uploadPath}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 打开路径浏览器
|
|
|
|
|
+ */
|
|
|
|
|
+ private openPathBrowser(): void {
|
|
|
|
|
+ if (!this.selectedAccount) {
|
|
|
|
|
+ Logger.warn(TAG, '请先选择账户');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.currentBrowsePath = this.uploadPath;
|
|
|
|
|
+ this.isShowPathBrowser = true;
|
|
|
|
|
+ this.loadWebDavFolders(this.currentBrowsePath);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 关闭路径浏览器
|
|
|
|
|
+ */
|
|
|
|
|
+ private closePathBrowser(): void {
|
|
|
|
|
+ this.isShowPathBrowser = false;
|
|
|
|
|
+ this.browseFolders = [];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 加载WebDAV文件夹列表
|
|
|
|
|
+ * @param path 路径
|
|
|
|
|
+ */
|
|
|
|
|
+ private async loadWebDavFolders(path: string): Promise<void> {
|
|
|
|
|
+ if (!this.selectedAccount) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.isLoadingFolders = true;
|
|
|
|
|
+ Logger.info(TAG, `加载文件夹列表: ${path}`);
|
|
|
|
|
+
|
|
|
|
|
+ await this.webdavManager.loadFilesInfoFromAccount(this.selectedAccount, path);
|
|
|
|
|
+
|
|
|
|
|
+ // 过滤出文件夹
|
|
|
|
|
+ const allFiles = this.webdavManager.webDavFiles;
|
|
|
|
|
+ this.browseFolders = [];
|
|
|
|
|
+ for (let i = 0; i < allFiles.length; i++) {
|
|
|
|
|
+ const file = allFiles[i];
|
|
|
|
|
+ if (file.isDirectory) {
|
|
|
|
|
+ this.browseFolders.push(file);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Logger.info(TAG, `加载了 ${this.browseFolders.length} 个文件夹`);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as Error;
|
|
|
|
|
+ Logger.error(TAG, `加载文件夹失败: ${err.message}`);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.isLoadingFolders = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 进入子文件夹
|
|
|
|
|
+ * @param folder 文件夹信息
|
|
|
|
|
+ */
|
|
|
|
|
+ private async enterBrowseFolder(folder: FileInfo): Promise<void> {
|
|
|
|
|
+ this.currentBrowsePath = folder.href;
|
|
|
|
|
+ await this.loadWebDavFolders(folder.href);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 返回上级文件夹
|
|
|
|
|
+ */
|
|
|
|
|
+ private async goBackBrowseFolder(): Promise<void> {
|
|
|
|
|
+ if (this.currentBrowsePath === '/') {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const lastSlashIndex = this.currentBrowsePath.lastIndexOf('/');
|
|
|
|
|
+ if (lastSlashIndex > 0) {
|
|
|
|
|
+ this.currentBrowsePath = this.currentBrowsePath.substring(0, lastSlashIndex);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.currentBrowsePath = '/';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await this.loadWebDavFolders(this.currentBrowsePath);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 确认选择路径
|
|
|
|
|
+ */
|
|
|
|
|
+ private confirmPathSelection(): void {
|
|
|
|
|
+ this.uploadPath = this.currentBrowsePath;
|
|
|
|
|
+ Logger.info(TAG, `选择上传路径: ${this.uploadPath}`);
|
|
|
|
|
+ this.closePathBrowser();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 开始上传
|
|
|
|
|
+ */
|
|
|
|
|
+ private async startUpload(): Promise<void> {
|
|
|
|
|
+ // 验证
|
|
|
|
|
+ if (!this.selectedAccount) {
|
|
|
|
|
+ Logger.warn(TAG, '请先选择账户');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (this.selectedFiles.length === 0) {
|
|
|
|
|
+ Logger.warn(TAG, '请先选择文件');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!this.uploadPath) {
|
|
|
|
|
+ Logger.warn(TAG, '请先选择上传路径');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ Logger.info(TAG, '开始上传任务');
|
|
|
|
|
+
|
|
|
|
|
+ // 构建VideoItem列表
|
|
|
|
|
+ const songs: VideoItem[] = [];
|
|
|
|
|
+ for (let i = 0; i < this.selectedFiles.length; i++) {
|
|
|
|
|
+ const fileUri = this.selectedFiles[i];
|
|
|
|
|
+ const fileName = this.selectedFileNames[i];
|
|
|
|
|
+
|
|
|
|
|
+ const videoItem = new VideoItem(
|
|
|
|
|
+ fileName,
|
|
|
|
|
+ fileUri,
|
|
|
|
|
+ fileUri,
|
|
|
|
|
+ CommonConstants.TYPE_LOCAL,
|
|
|
|
|
+ 0,
|
|
|
|
|
+ '',
|
|
|
|
|
+ undefined,
|
|
|
|
|
+ undefined,
|
|
|
|
|
+ undefined,
|
|
|
|
|
+ undefined,
|
|
|
|
|
+ undefined,
|
|
|
|
|
+ fileName
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ songs.push(videoItem);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 添加到上传队列
|
|
|
|
|
+ this.webdavManager.addToUploadQueue(songs, this.selectedAccount);
|
|
|
|
|
+ this.totalUploadCount = songs.length;
|
|
|
|
|
+ this.uploadedCount = 0;
|
|
|
|
|
+
|
|
|
|
|
+ // 开始处理上传队列
|
|
|
|
|
+ await this.webdavManager.startUploadQueue();
|
|
|
|
|
+
|
|
|
|
|
+ Logger.info(TAG, '上传任务已启动');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as Error;
|
|
|
|
|
+ Logger.error(TAG, `启动上传失败: ${err.message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 暂停上传
|
|
|
|
|
+ */
|
|
|
|
|
+ private pauseUpload(): void {
|
|
|
|
|
+ this.webdavManager.pauseUploadQueue();
|
|
|
|
|
+ Logger.info(TAG, '暂停上传');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 恢复上传
|
|
|
|
|
+ */
|
|
|
|
|
+ private async resumeUpload(): Promise<void> {
|
|
|
|
|
+ await this.webdavManager.resumeUploadQueue();
|
|
|
|
|
+ Logger.info(TAG, '恢复上传');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 取消上传
|
|
|
|
|
+ */
|
|
|
|
|
+ private cancelUpload(): void {
|
|
|
|
|
+ this.webdavManager.pauseUploadQueue();
|
|
|
|
|
+ this.webdavManager.clearUploadQueue();
|
|
|
|
|
+ this.isUploading = false;
|
|
|
|
|
+ this.currentTask = null;
|
|
|
|
|
+ Logger.info(TAG, '取消上传');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 上传进度显示组件
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ UploadProgressView() {
|
|
|
|
|
+ if (!this.isUploading || !this.currentTask) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ Text('上传进度')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .margin({ bottom: 12 })
|
|
|
|
|
+ .alignSelf(ItemAlign.Start)
|
|
|
|
|
+
|
|
|
|
|
+ // 当前文件名
|
|
|
|
|
+ Text(`当前文件: ${this.currentTask.name}`)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#99FFFFFF' : '#99000000')
|
|
|
|
|
+ .margin({ bottom: 8 })
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+
|
|
|
|
|
+ // 进度条
|
|
|
|
|
+ Progress({ value: this.uploadProgress, total: 100, type: ProgressType.Linear })
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .color(this.themeColor)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#33FFFFFF' : '#19000000')
|
|
|
|
|
+ .margin({ bottom: 8 })
|
|
|
|
|
+
|
|
|
|
|
+ // 进度百分比和速度信息行
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ Text(`${this.uploadProgress}%`)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor(this.themeColor)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+
|
|
|
|
|
+ Text(`${this.uploadSpeed}`)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .margin({ bottom: 8 })
|
|
|
|
|
+
|
|
|
|
|
+ // 已上传数量
|
|
|
|
|
+ Text(`已上传: ${this.uploadedCount}/${this.totalUploadCount}`)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#99FFFFFF' : '#99000000')
|
|
|
|
|
+ .margin({ bottom: 16 })
|
|
|
|
|
+
|
|
|
|
|
+ // 控制按钮
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ Button(this.webdavManager.isPauseUpload ? '恢复' : '暂停')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .height(44)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .backgroundColor(this.themeColor)
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ if (this.webdavManager.isPauseUpload) {
|
|
|
|
|
+ this.resumeUpload();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.pauseUpload();
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ Button('取消')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .height(44)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#D94838' : '#E84026')
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.cancelUpload();
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: this.isDarkMode ? 8 : 12,
|
|
|
|
|
+ color: this.isDarkMode ? '#0C000000' : '#19000000',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 2
|
|
|
|
|
+ })
|
|
|
|
|
+ .margin({ bottom: 16 })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 上传队列管理组件(使用LazyDataSource优化性能)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ UploadQueueView() {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ Text('上传队列')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .margin({ bottom: 12 })
|
|
|
|
|
+ .alignSelf(ItemAlign.Start)
|
|
|
|
|
+
|
|
|
|
|
+ Tabs({ barPosition: BarPosition.Start }) {
|
|
|
|
|
+ TabContent() {
|
|
|
|
|
+ this.QueueList(this.uploadQueueDataSource, 'pending')
|
|
|
|
|
+ }
|
|
|
|
|
+ .tabBar(`待上传 (${this.uploadQueue.length})`)
|
|
|
|
|
+
|
|
|
|
|
+ TabContent() {
|
|
|
|
|
+ this.QueueList(this.finishQueueDataSource, 'finished')
|
|
|
|
|
+ }
|
|
|
|
|
+ .tabBar(`已完成 (${this.finishQueue.length})`)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(300)
|
|
|
|
|
+ .barMode(BarMode.Fixed)
|
|
|
|
|
+ .barBackgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ .animationDuration(300)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: this.isDarkMode ? 8 : 12,
|
|
|
|
|
+ color: this.isDarkMode ? '#0C000000' : '#19000000',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 2
|
|
|
|
|
+ })
|
|
|
|
|
+ .margin({ bottom: 16 })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 队列列表组件(使用LazyForEach优化性能)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ QueueList(dataSource: UploadTaskDataSource, type: string) {
|
|
|
|
|
+ if (dataSource.totalCount() === 0) {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ Text(type === 'pending' ? '暂无待上传任务' : '暂无已完成任务')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ List({ space: 8 }) {
|
|
|
|
|
+ LazyForEach(dataSource, (item: VideoItem, index: number) => {
|
|
|
|
|
+ ListItem() {
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ // 文件图标
|
|
|
|
|
+ Text('🎵')
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+
|
|
|
|
|
+ Text(item.name)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+
|
|
|
|
|
+ if (type === 'pending') {
|
|
|
|
|
+ Button('移除')
|
|
|
|
|
+ .fontSize(12)
|
|
|
|
|
+ .height(32)
|
|
|
|
|
+ .padding({ left: 12, right: 12 })
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#D94838' : '#E84026')
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .borderRadius(6)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ // 从队列移除
|
|
|
|
|
+ const tasks = this.webdavManager.uploadQueue;
|
|
|
|
|
+ if (index < tasks.length) {
|
|
|
|
|
+ this.webdavManager.removeFromUploadQueue(tasks[index]);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Text('✓')
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#5BA854' : '#64BB5C')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(12)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#2E3033' : '#F1F3F5')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, (item: VideoItem, index: number) => `${type}-${index}-${item.name}`)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .cachedCount(5) // 缓存5个列表项以提升滚动性能
|
|
|
|
|
+ .divider({
|
|
|
|
|
+ strokeWidth: 1,
|
|
|
|
|
+ color: this.isDarkMode ? '#19FFFFFF' : '#0C000000'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 开始上传按钮组件
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ StartUploadButton() {
|
|
|
|
|
+ Button('开始上传')
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(52)
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Bold)
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .backgroundColor(this.themeColor)
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .enabled(!this.isUploading && this.selectedFiles.length > 0 && this.selectedAccount !== null)
|
|
|
|
|
+ .opacity((!this.isUploading && this.selectedFiles.length > 0 && this.selectedAccount !== null) ? 1.0 : 0.5)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: 12,
|
|
|
|
|
+ color: this.themeColor + '40',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 4
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.startUpload();
|
|
|
|
|
+ })
|
|
|
|
|
+ .margin({ bottom: 16 })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 上传配置区域组件
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ UploadConfigSection() {
|
|
|
|
|
+ Column({ space: 12 }) {
|
|
|
|
|
+ Text('上传配置')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .alignSelf(ItemAlign.Start)
|
|
|
|
|
+
|
|
|
|
|
+ // 上传路径选择
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ Column({ space: 4 }) {
|
|
|
|
|
+ Text('上传路径')
|
|
|
|
|
+ .fontSize(12)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+
|
|
|
|
|
+ Text(this.uploadPath || '请选择路径')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+ }
|
|
|
|
|
+ .alignItems(HorizontalAlign.Start)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+
|
|
|
|
|
+ Button('浏览')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .height(40)
|
|
|
|
|
+ .padding({ left: 16, right: 16 })
|
|
|
|
|
+ .backgroundColor(this.themeColor)
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .enabled(this.selectedAccount !== null)
|
|
|
|
|
+ .opacity(this.selectedAccount !== null ? 1.0 : 0.5)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.openPathBrowser();
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#2E3033' : '#F1F3F5')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+
|
|
|
|
|
+ // 重复文件处理方式
|
|
|
|
|
+ Column({ space: 8 }) {
|
|
|
|
|
+ Text('重复文件处理')
|
|
|
|
|
+ .fontSize(12)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+ .alignSelf(ItemAlign.Start)
|
|
|
|
|
+
|
|
|
|
|
+ Row({ space: 16 }) {
|
|
|
|
|
+ Row({ space: 6 }) {
|
|
|
|
|
+ Radio({ value: 'skip', group: 'duplicateAction' })
|
|
|
|
|
+ .checked(this.duplicateAction === 'skip')
|
|
|
|
|
+ .radioStyle({
|
|
|
|
|
+ checkedBackgroundColor: this.themeColor
|
|
|
|
|
+ })
|
|
|
|
|
+ .onChange((checked: boolean) => {
|
|
|
|
|
+ if (checked) {
|
|
|
|
|
+ this.duplicateAction = 'skip';
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ Text('跳过')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Row({ space: 6 }) {
|
|
|
|
|
+ Radio({ value: 'overwrite', group: 'duplicateAction' })
|
|
|
|
|
+ .checked(this.duplicateAction === 'overwrite')
|
|
|
|
|
+ .radioStyle({
|
|
|
|
|
+ checkedBackgroundColor: this.themeColor
|
|
|
|
|
+ })
|
|
|
|
|
+ .onChange((checked: boolean) => {
|
|
|
|
|
+ if (checked) {
|
|
|
|
|
+ this.duplicateAction = 'overwrite';
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ Text('覆盖')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Row({ space: 6 }) {
|
|
|
|
|
+ Radio({ value: 'rename', group: 'duplicateAction' })
|
|
|
|
|
+ .checked(this.duplicateAction === 'rename')
|
|
|
|
|
+ .radioStyle({
|
|
|
|
|
+ checkedBackgroundColor: this.themeColor
|
|
|
|
|
+ })
|
|
|
|
|
+ .onChange((checked: boolean) => {
|
|
|
|
|
+ if (checked) {
|
|
|
|
|
+ this.duplicateAction = 'rename';
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ Text('重命名')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#2E3033' : '#F1F3F5')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: this.isDarkMode ? 8 : 12,
|
|
|
|
|
+ color: this.isDarkMode ? '#0C000000' : '#19000000',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 2
|
|
|
|
|
+ })
|
|
|
|
|
+ .margin({ bottom: 16 })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 路径浏览器对话框
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ PathBrowserDialog() {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ // 标题栏
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ Text('选择上传路径')
|
|
|
|
|
+ .fontSize(18)
|
|
|
|
|
+ .fontWeight(FontWeight.Bold)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+
|
|
|
|
|
+ Button('关闭')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .height(36)
|
|
|
|
|
+ .padding({ left: 16, right: 16 })
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#2E3033' : '#E5E5EA')
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.closePathBrowser();
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+
|
|
|
|
|
+ // 当前路径显示
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ Column({ space: 4 }) {
|
|
|
|
|
+ Text('当前路径')
|
|
|
|
|
+ .fontSize(12)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+
|
|
|
|
|
+ Text(this.currentBrowsePath)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+ }
|
|
|
|
|
+ .alignItems(HorizontalAlign.Start)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+
|
|
|
|
|
+ if (this.currentBrowsePath !== '/') {
|
|
|
|
|
+ Button('返回上级')
|
|
|
|
|
+ .fontSize(12)
|
|
|
|
|
+ .height(36)
|
|
|
|
|
+ .padding({ left: 12, right: 12 })
|
|
|
|
|
+ .backgroundColor(this.themeColor)
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.goBackBrowseFolder();
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#191A1C' : '#F1F3F5')
|
|
|
|
|
+
|
|
|
|
|
+ // 文件夹列表
|
|
|
|
|
+ if (this.isLoadingFolders) {
|
|
|
|
|
+ Column({ space: 12 }) {
|
|
|
|
|
+ LoadingProgress()
|
|
|
|
|
+ .width(48)
|
|
|
|
|
+ .height(48)
|
|
|
|
|
+ .color(this.themeColor)
|
|
|
|
|
+
|
|
|
|
|
+ Text('加载中...')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#99FFFFFF' : '#99000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(300)
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ } else if (this.browseFolders.length === 0) {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ Text('📂')
|
|
|
|
|
+ .fontSize(48)
|
|
|
|
|
+ .margin({ bottom: 12 })
|
|
|
|
|
+
|
|
|
|
|
+ Text('当前目录没有子文件夹')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(300)
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ List({ space: 8 }) {
|
|
|
|
|
+ ForEach(this.browseFolders, (folder: FileInfo, index: number) => {
|
|
|
|
|
+ ListItem() {
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ Text('📁')
|
|
|
|
|
+ .fontSize(24)
|
|
|
|
|
+
|
|
|
|
|
+ Text(folder.fileName)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+
|
|
|
|
|
+ Text('→')
|
|
|
|
|
+ .fontSize(18)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#2E3033' : '#F1F3F5')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.enterBrowseFolder(folder);
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }, (folder: FileInfo, index: number) => `folder-${index}-${folder.fileName}`)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(300)
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ .divider({
|
|
|
|
|
+ strokeWidth: 1,
|
|
|
|
|
+ color: this.isDarkMode ? '#19FFFFFF' : '#0C000000'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 底部按钮
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ Button('取消')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .height(48)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#2E3033' : '#E5E5EA')
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.closePathBrowser();
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ Button('选择此路径')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .height(48)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .backgroundColor(this.themeColor)
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.confirmPathSelection();
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('90%')
|
|
|
|
|
+ .maxWidth(600)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#191A1C' : '#FFFFFF')
|
|
|
|
|
+ .borderRadius(16)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: 24,
|
|
|
|
|
+ color: this.isDarkMode ? '#33000000' : '#19000000',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 8
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 账户选择区域组件
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ AccountSelectorSection() {
|
|
|
|
|
+ Column({ space: 12 }) {
|
|
|
|
|
+ Text('选择WebDAV账户')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .alignSelf(ItemAlign.Start)
|
|
|
|
|
+
|
|
|
|
|
+ if (this.accounts.length === 0) {
|
|
|
|
|
+ Column({ space: 12 }) {
|
|
|
|
|
+ Text('📦')
|
|
|
|
|
+ .fontSize(48)
|
|
|
|
|
+
|
|
|
|
|
+ Text('暂无WebDAV账户')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#99FFFFFF' : '#99000000')
|
|
|
|
|
+
|
|
|
|
|
+ Text('请先添加账户')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(32)
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ List({ space: 8 }) {
|
|
|
|
|
+ ForEach(this.accounts, (account: WebDavAccount, index: number) => {
|
|
|
|
|
+ ListItem() {
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ // 账户图标
|
|
|
|
|
+ Text('☁️')
|
|
|
|
|
+ .fontSize(24)
|
|
|
|
|
+
|
|
|
|
|
+ Column({ space: 4 }) {
|
|
|
|
|
+ Text(account.name)
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+
|
|
|
|
|
+ Text(`${account.host}:${account.port}`)
|
|
|
|
|
+ .fontSize(12)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#66FFFFFF' : '#66000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ .alignItems(HorizontalAlign.Start)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+
|
|
|
|
|
+ if (this.selectedAccount && this.selectedAccount.id === account.id) {
|
|
|
|
|
+ Text('✓')
|
|
|
|
|
+ .fontSize(24)
|
|
|
|
|
+ .fontColor(this.themeColor)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.selectedAccount && this.selectedAccount.id === account.id
|
|
|
|
|
+ ? (this.isDarkMode ? '#2E3033' : this.themeColor + '1A')
|
|
|
|
|
+ : (this.isDarkMode ? '#2E3033' : '#F1F3F5'))
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ .border({
|
|
|
|
|
+ width: 2,
|
|
|
|
|
+ color: this.selectedAccount && this.selectedAccount.id === account.id
|
|
|
|
|
+ ? this.themeColor
|
|
|
|
|
+ : 'transparent'
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.selectUploadAccount(account);
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }, (account: WebDavAccount) => `account-${account.id}`)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .divider({
|
|
|
|
|
+ strokeWidth: 1,
|
|
|
|
|
+ color: this.isDarkMode ? '#19FFFFFF' : '#0C000000'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: this.isDarkMode ? 8 : 12,
|
|
|
|
|
+ color: this.isDarkMode ? '#0C000000' : '#19000000',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 2
|
|
|
|
|
+ })
|
|
|
|
|
+ .margin({ bottom: 16 })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 文件选择区域组件
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ FilePickerSection() {
|
|
|
|
|
+ Column({ space: 12 }) {
|
|
|
|
|
+ Text('选择文件')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .alignSelf(ItemAlign.Start)
|
|
|
|
|
+
|
|
|
|
|
+ Button('选择音频文件')
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(52)
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .backgroundColor(this.themeColor)
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.openFilePicker();
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ if (this.selectedFiles.length > 0) {
|
|
|
|
|
+ Row({ space: 8 }) {
|
|
|
|
|
+ Text('✓')
|
|
|
|
|
+ .fontSize(18)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#5BA854' : '#64BB5C')
|
|
|
|
|
+
|
|
|
|
|
+ Text(`已选择 ${this.selectedFiles.length} 个文件`)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#99FFFFFF' : '#99000000')
|
|
|
|
|
+ }
|
|
|
|
|
+ .padding({ top: 4 })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: this.isDarkMode ? 8 : 12,
|
|
|
|
|
+ color: this.isDarkMode ? '#0C000000' : '#19000000',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 2
|
|
|
|
|
+ })
|
|
|
|
|
+ .margin({ bottom: 16 })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 已选文件列表组件
|
|
|
|
|
+ */
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ SelectedFilesList() {
|
|
|
|
|
+ Column({ space: 12 }) {
|
|
|
|
|
+ Text('已选文件')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .alignSelf(ItemAlign.Start)
|
|
|
|
|
+
|
|
|
|
|
+ List({ space: 8 }) {
|
|
|
|
|
+ ForEach(this.selectedFileNames, (fileName: string, index: number) => {
|
|
|
|
|
+ ListItem() {
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ // 文件图标
|
|
|
|
|
+ Text('🎵')
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+
|
|
|
|
|
+ Text(fileName)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+
|
|
|
|
|
+ Button('移除')
|
|
|
|
|
+ .fontSize(12)
|
|
|
|
|
+ .height(32)
|
|
|
|
|
+ .padding({ left: 12, right: 12 })
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#D94838' : '#E84026')
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ .borderRadius(6)
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.removeSelectedFile(index);
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(12)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#2E3033' : '#F1F3F5')
|
|
|
|
|
+ .borderRadius(8)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, (fileName: string, index: number) => `${index}-${fileName}`)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .maxHeight(300)
|
|
|
|
|
+ .divider({
|
|
|
|
|
+ strokeWidth: 1,
|
|
|
|
|
+ color: this.isDarkMode ? '#19FFFFFF' : '#0C000000'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: this.isDarkMode ? 8 : 12,
|
|
|
|
|
+ color: this.isDarkMode ? '#0C000000' : '#19000000',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 2
|
|
|
|
|
+ })
|
|
|
|
|
+ .margin({ bottom: 16 })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ build() {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ // 标题栏
|
|
|
|
|
+ Row({ space: 12 }) {
|
|
|
|
|
+ Button({ type: ButtonType.Circle }) {
|
|
|
|
|
+ Text('←')
|
|
|
|
|
+ .fontSize(24)
|
|
|
|
|
+ .fontColor('#FFFFFF')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width(44)
|
|
|
|
|
+ .height(44)
|
|
|
|
|
+ .backgroundColor(this.themeColor)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: 8,
|
|
|
|
|
+ color: this.themeColor + '40',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 2
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ router.back();
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ Text('上传音乐到WebDAV')
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontWeight(FontWeight.Bold)
|
|
|
|
|
+ .fontColor(this.isDarkMode ? '#E5FFFFFF' : '#E5000000')
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(56)
|
|
|
|
|
+ .padding({ left: 16, right: 16 })
|
|
|
|
|
+ .margin({ top: this.topSafeHeight })
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+
|
|
|
|
|
+ // 页面内容区域
|
|
|
|
|
+ Scroll() {
|
|
|
|
|
+ Column({ space: 0 }) {
|
|
|
|
|
+ // 账户选择区域
|
|
|
|
|
+ this.AccountSelectorSection()
|
|
|
|
|
+
|
|
|
|
|
+ // 上传配置区域
|
|
|
|
|
+ this.UploadConfigSection()
|
|
|
|
|
+
|
|
|
|
|
+ // 文件选择区域
|
|
|
|
|
+ this.FilePickerSection()
|
|
|
|
|
+
|
|
|
|
|
+ // 已选文件列表
|
|
|
|
|
+ if (this.selectedFiles.length > 0) {
|
|
|
|
|
+ this.SelectedFilesList()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 开始上传按钮
|
|
|
|
|
+ if (!this.isUploading) {
|
|
|
|
|
+ this.StartUploadButton()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 上传进度显示
|
|
|
|
|
+ this.UploadProgressView()
|
|
|
|
|
+
|
|
|
|
|
+ // 上传队列管理
|
|
|
|
|
+ if (this.uploadQueue.length > 0 || this.finishQueue.length > 0) {
|
|
|
|
|
+ this.UploadQueueView()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .constraintSize({ minHeight: '100%' })
|
|
|
|
|
+ }
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(16)
|
|
|
|
|
+ .scrollBar(BarState.Auto)
|
|
|
|
|
+
|
|
|
|
|
+ // 底部安全区
|
|
|
|
|
+ Row()
|
|
|
|
|
+ .height(this.bottomSafeHeight)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#23272E' : '#FFFFFF')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? '#191A1C' : '#F1F3F5')
|
|
|
|
|
+ .bindContentCover(this.isShowPathBrowser, this.PathBrowserDialogBuilder(), {
|
|
|
|
|
+ modalTransition: ModalTransition.DEFAULT,
|
|
|
|
|
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
|
|
|
|
+ onDisappear: () => {
|
|
|
|
|
+ this.isShowPathBrowser = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ PathBrowserDialogBuilder() {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ this.PathBrowserDialog()
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ .backgroundColor('rgba(0, 0, 0, 0.5)')
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ // 点击背景关闭对话框
|
|
|
|
|
+ this.closePathBrowser();
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|