|
|
@@ -1,4 +1,4 @@
|
|
|
-import { BreadcrumbItem, RemoteDriveManager } from '../common/util/RemoteDriveManager';
|
|
|
+import { BreadcrumbItem, RemoteDriveGlobalSearchResult, RemoteDriveManager } from '../common/util/RemoteDriveManager';
|
|
|
import { WebDavAccount } from '../viewmodel/WebDavAccount';
|
|
|
import { Song } from '../viewmodel/Song';
|
|
|
import { RemoteDriveManagerStates } from '../common/enums/RemoteDriveManagerStates';
|
|
|
@@ -111,6 +111,7 @@ export struct WebDavMainPage {
|
|
|
private listScroller: ListScroller = new ListScroller()
|
|
|
@StorageProp('bottomSafeHeight') bottomSafeHeight: number = 0;
|
|
|
searchController: SearchController = new SearchController()
|
|
|
+ private searchTicket: number = 0;
|
|
|
@StorageProp('animationState') animationState: AnimationStatus= AnimationStatus.Running
|
|
|
@State isNoJumpToHome: boolean = false //网盘播放不跳转首页
|
|
|
@State isSearchMode: boolean = false
|
|
|
@@ -145,6 +146,7 @@ export struct WebDavMainPage {
|
|
|
@State rootPath: string = ''
|
|
|
@State isShowDownloadCenter: boolean = false
|
|
|
@State downloadCenterTabIndex: number[] = [0]
|
|
|
+ @State isSearchLoading: boolean = false
|
|
|
private readonly downloadFolderName: string = '下载'
|
|
|
private readonly metadataUpdateEvent: emitter.InnerEvent = { eventId: EventConstants.EVENT_WEBDAV_METADATA_UPDATED };
|
|
|
private thumbnailTaskToken: number = 0;
|
|
|
@@ -165,6 +167,12 @@ export struct WebDavMainPage {
|
|
|
console.log('heanup 切换账户:', this.selectedAccount.name);
|
|
|
this.thumbnailTaskToken += 1;
|
|
|
this.thumbnailRunningKeys.clear();
|
|
|
+ this.searchTicket++;
|
|
|
+ this.searchText = '';
|
|
|
+ this.filteredList = [];
|
|
|
+ this.filteredFolderList = [];
|
|
|
+ this.isSearchMode = false;
|
|
|
+ this.isSearchLoading = false;
|
|
|
this.songs = [];
|
|
|
this.visibleFoldersState = [];
|
|
|
this.updateListData(this.songs)
|
|
|
@@ -187,7 +195,12 @@ export struct WebDavMainPage {
|
|
|
if (!noSort) {
|
|
|
this.sortType = PreferencesUtil.getNumberSync('webDavSortType', 0)
|
|
|
this.doSortType(this.sortType)
|
|
|
+ return
|
|
|
}
|
|
|
+ this.refreshDisplaySongs(mList)
|
|
|
+ }
|
|
|
+
|
|
|
+ private refreshDisplaySongs(mList: Array<VideoItem>): void {
|
|
|
this.dataSource.pushArrayData(mList)
|
|
|
if(mList.length > 0){
|
|
|
setTimeout(() => {
|
|
|
@@ -1523,6 +1536,11 @@ export struct WebDavMainPage {
|
|
|
this.breadcrumbs = this.webdavManager.getBreadcrumbs();
|
|
|
this.syncSelectionAfterRefresh();
|
|
|
this.scheduleRemoteThumbPrefetch();
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
+ void this.applyGlobalSearch(this.searchText, ++this.searchTicket);
|
|
|
+ } else {
|
|
|
+ this.restoreCurrentDirectorySearchView();
|
|
|
+ }
|
|
|
|
|
|
// promptAction.showToast({
|
|
|
// message: '加载成功: ' + this.webDavFiles.filter(f => f.isDirectory).length + '个文件夹, ' + this.songs.length + '首歌曲'
|
|
|
@@ -1537,6 +1555,16 @@ export struct WebDavMainPage {
|
|
|
case RemoteDriveManagerStates.RemoveAccountSucceed:
|
|
|
this.loadAccounts();
|
|
|
break;
|
|
|
+ case RemoteDriveManagerStates.GlobalSearchIndexBuildStart:
|
|
|
+ case RemoteDriveManagerStates.GlobalSearchIndexUpdated:
|
|
|
+ case RemoteDriveManagerStates.GlobalSearchIndexReady:
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
+ void this.applyGlobalSearch(this.searchText, ++this.searchTicket);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case RemoteDriveManagerStates.GlobalSearchIndexFailed:
|
|
|
+ this.isSearchLoading = false;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2092,53 +2120,78 @@ export struct WebDavMainPage {
|
|
|
}.attributeModifier(new MenuModifier())
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- doSortType(index: number) {
|
|
|
- this.sortType = index;
|
|
|
- // 对歌曲列表进行排序
|
|
|
+ private sortSongsForType(target: Array<VideoItem>, index: number): Array<VideoItem> {
|
|
|
switch (index) {
|
|
|
case 0:
|
|
|
- Utility.doSortListAscending(this.songs,this.isShowFileName)
|
|
|
- this.visibleFoldersState.sort((a, b) => {
|
|
|
- return a.fileName.localeCompare(b.fileName);
|
|
|
- });
|
|
|
+ Utility.doSortListAscending(target, this.isShowFileName)
|
|
|
break;
|
|
|
case 1:
|
|
|
- Utility.doSortListDescending(this.songs,this.isShowFileName)
|
|
|
-
|
|
|
- this.visibleFoldersState.sort((a, b) => {
|
|
|
- return b.fileName.localeCompare(a.fileName);
|
|
|
- });
|
|
|
+ Utility.doSortListDescending(target, this.isShowFileName)
|
|
|
break;
|
|
|
case 2:
|
|
|
- this.songs.sort((a, b) => {
|
|
|
+ target.sort((a, b) => {
|
|
|
return a.cTime.localeCompare(b.cTime);
|
|
|
});
|
|
|
- this.visibleFoldersState.sort((a, b) => {
|
|
|
- return a.time-b.time;
|
|
|
- });
|
|
|
break;
|
|
|
case 3:
|
|
|
- this.songs.sort((a, b) => {
|
|
|
+ target.sort((a, b) => {
|
|
|
return b.cTime.localeCompare(a.cTime);
|
|
|
});
|
|
|
- this.visibleFoldersState.sort((a, b) => {
|
|
|
- return b.time-a.time;
|
|
|
- });
|
|
|
break;
|
|
|
case 4:
|
|
|
- this.songs.sort((a, b) => {
|
|
|
+ target.sort((a, b) => {
|
|
|
return a.videoSize - b.videoSize;
|
|
|
});
|
|
|
break;
|
|
|
case 5:
|
|
|
- this.songs.sort((a, b) => {
|
|
|
+ target.sort((a, b) => {
|
|
|
return b.videoSize - a.videoSize;
|
|
|
});
|
|
|
break;
|
|
|
}
|
|
|
+ return target;
|
|
|
+ }
|
|
|
|
|
|
-
|
|
|
+ private sortFoldersForType(target: Array<FileInfo>, index: number): Array<FileInfo> {
|
|
|
+ switch (index) {
|
|
|
+ case 0:
|
|
|
+ target.sort((a, b) => {
|
|
|
+ return a.fileName.localeCompare(b.fileName);
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ target.sort((a, b) => {
|
|
|
+ return b.fileName.localeCompare(a.fileName);
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ target.sort((a, b) => {
|
|
|
+ return a.time - b.time;
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ target.sort((a, b) => {
|
|
|
+ return b.time - a.time;
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return target;
|
|
|
+ }
|
|
|
+
|
|
|
+ doSortType(index: number) {
|
|
|
+ this.sortType = index;
|
|
|
+ this.sortSongsForType(this.songs, index)
|
|
|
+ this.sortFoldersForType(this.visibleFoldersState, index)
|
|
|
+ if (this.filteredList.length > 0) {
|
|
|
+ this.filteredList = this.sortSongsForType([...this.filteredList], index)
|
|
|
+ }
|
|
|
+ if (this.filteredFolderList.length > 0) {
|
|
|
+ this.filteredFolderList = this.sortFoldersForType([...this.filteredFolderList], index)
|
|
|
+ }
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
+ this.refreshDisplaySongs(this.filteredList)
|
|
|
+ return
|
|
|
+ }
|
|
|
this.updateListData(this.songs,true)
|
|
|
}
|
|
|
|
|
|
@@ -2191,6 +2244,7 @@ export struct WebDavMainPage {
|
|
|
.onClick(() => {
|
|
|
if(this.isSearchMode){
|
|
|
this.isSearchMode = false
|
|
|
+ this.searchController.stopEditing()
|
|
|
this.onSearchInput('')
|
|
|
}else{
|
|
|
if(this.webdavManager.canGoBack()){
|
|
|
@@ -2216,7 +2270,7 @@ export struct WebDavMainPage {
|
|
|
}
|
|
|
|
|
|
//搜索框
|
|
|
- Search({ controller: this.searchController,value: this.searchText, placeholder: '搜索标题、艺术家...' })
|
|
|
+ Search({ controller: this.searchController,value: this.searchText, placeholder: '输入名称...' })
|
|
|
.searchButton('搜索',{fontColor:this.themeColor})
|
|
|
.searchIcon({
|
|
|
src: $r('sys.media.ohos_ic_public_search_filled')
|
|
|
@@ -2237,7 +2291,7 @@ export struct WebDavMainPage {
|
|
|
.onSubmit((value: string) => {
|
|
|
console.log('onecold onSubmit ='+value)
|
|
|
this.searchController.stopEditing()
|
|
|
- this.onSearchInput(this.searchText);
|
|
|
+ this.onSearchInput(value);
|
|
|
|
|
|
})
|
|
|
.onChange((value: string) => {
|
|
|
@@ -2260,8 +2314,9 @@ export struct WebDavMainPage {
|
|
|
.attributeModifier(new ShadowModifier())
|
|
|
.zIndex(0)
|
|
|
.onClick(()=>{
|
|
|
- this.filteredFolderList = [...this.visibleFoldersState]; // 显示所有文件夹
|
|
|
this.isSearchMode = true
|
|
|
+ this.restoreCurrentDirectorySearchView()
|
|
|
+ void this.webdavManager.ensureGlobalSearchIndex(this.selectedAccount)
|
|
|
})
|
|
|
//排序按钮
|
|
|
Button({ type: ButtonType.Circle, stateEffect: true }) {
|
|
|
@@ -2359,36 +2414,75 @@ export struct WebDavMainPage {
|
|
|
@State filteredList: Array<VideoItem> = []; // 过滤后的歌曲结果
|
|
|
@State filteredFolderList: Array<FileInfo> = []; // 过滤后的文件夹结果
|
|
|
|
|
|
- // 实时搜索逻辑(带防抖)
|
|
|
-// 实时搜索逻辑(带防抖)
|
|
|
- private onSearchInput(value: string) {
|
|
|
- this.searchText = value.trim();
|
|
|
- let mSearchList: Array<VideoItem> = []
|
|
|
- mSearchList = this.songs
|
|
|
-
|
|
|
- // 新增条件判断:空输入时显示所有数据
|
|
|
- if (this.searchText === '') {
|
|
|
- this.filteredList = [...mSearchList]; // 创建新数组以触发状态更新
|
|
|
- this.filteredFolderList = [...this.visibleFoldersState]; // 显示所有文件夹
|
|
|
- } else {
|
|
|
- this.filteredList = mSearchList.filter((item: VideoItem) => {
|
|
|
- //支持模糊匹配和艺术家 专辑匹配
|
|
|
- const regex = new RegExp(this.searchText.toLowerCase().replace(/\s+/g, '.*'), 'i');
|
|
|
- return regex.test(item.name.toLowerCase())||
|
|
|
- regex.test(item.fileName?.toLowerCase() ?? "") ||
|
|
|
- regex.test(item.artist?.toLowerCase() ?? "") ||
|
|
|
- regex.test(item.album?.toLowerCase() ?? "")
|
|
|
- });
|
|
|
-
|
|
|
- // 对文件夹进行过滤
|
|
|
- this.filteredFolderList = this.visibleFoldersState.filter((folder: FileInfo) => {
|
|
|
- const regex = new RegExp(this.searchText.toLowerCase().replace(/\s+/g, '.*'), 'i');
|
|
|
- return regex.test(folder.fileName?.toLowerCase() ?? "") ||
|
|
|
- regex.test(decodeUrlEncodedString(folder.fileName?.replace('/', '') ?? "").toLowerCase());
|
|
|
- });
|
|
|
+ private restoreCurrentDirectorySearchView(): void {
|
|
|
+ this.filteredList = this.sortSongsForType([...this.songs], this.sortType)
|
|
|
+ this.filteredFolderList = this.sortFoldersForType([...this.visibleFoldersState], this.sortType)
|
|
|
+ this.isSearchLoading = false
|
|
|
+ this.refreshDisplaySongs(this.filteredList)
|
|
|
+ }
|
|
|
+
|
|
|
+ private shouldShowSearchLocation(): boolean {
|
|
|
+ return this.isSearchMode && this.searchText.length > 0
|
|
|
+ }
|
|
|
+
|
|
|
+ private getSongSearchLocation(song: VideoItem): string {
|
|
|
+ if (!this.shouldShowSearchLocation() || !this.selectedAccount) {
|
|
|
+ return ''
|
|
|
}
|
|
|
+ return this.webdavManager.getSongSearchLocationLabel(this.selectedAccount, song)
|
|
|
+ }
|
|
|
+
|
|
|
+ private getFolderSearchLocation(folder: FileInfo): string {
|
|
|
+ if (!this.shouldShowSearchLocation() || !this.selectedAccount) {
|
|
|
+ return ''
|
|
|
+ }
|
|
|
+ return this.webdavManager.getFolderSearchLocationLabel(this.selectedAccount, folder)
|
|
|
+ }
|
|
|
|
|
|
- this.updateListData(this.filteredList);
|
|
|
+ private async applyGlobalSearch(keyword: string, ticket: number): Promise<void> {
|
|
|
+ if (!this.selectedAccount) {
|
|
|
+ if (ticket === this.searchTicket) {
|
|
|
+ this.isSearchLoading = false
|
|
|
+ this.filteredList = []
|
|
|
+ this.filteredFolderList = []
|
|
|
+ this.refreshDisplaySongs([])
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ const result: RemoteDriveGlobalSearchResult = await this.webdavManager.searchGlobalIndex(this.selectedAccount, keyword);
|
|
|
+ if (ticket !== this.searchTicket) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.filteredList = this.sortSongsForType([...result.songs], this.sortType)
|
|
|
+ this.filteredFolderList = this.sortFoldersForType([...result.folders], this.sortType)
|
|
|
+ this.isSearchLoading = result.isIndexing
|
|
|
+ this.refreshDisplaySongs(this.filteredList)
|
|
|
+ } catch (error) {
|
|
|
+ if (ticket !== this.searchTicket) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.isSearchLoading = false
|
|
|
+ this.filteredList = []
|
|
|
+ this.filteredFolderList = []
|
|
|
+ this.refreshDisplaySongs([])
|
|
|
+ ToastUtil.showToast(`搜索失败: ${(error as Error).message}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private onSearchInput(value: string) {
|
|
|
+ const keyword = value.trim();
|
|
|
+ this.searchText = keyword;
|
|
|
+ const ticket = ++this.searchTicket;
|
|
|
+ if (keyword.length === 0) {
|
|
|
+ this.restoreCurrentDirectorySearchView();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!this.isSearchMode) {
|
|
|
+ this.isSearchMode = true;
|
|
|
+ }
|
|
|
+ this.isSearchLoading = true;
|
|
|
+ void this.applyGlobalSearch(keyword, ticket);
|
|
|
}
|
|
|
|
|
|
private openSongPropertySheet(song: VideoItem): void {
|
|
|
@@ -2691,6 +2785,20 @@ export struct WebDavMainPage {
|
|
|
curve: Curve.Smooth // 可选动画曲线
|
|
|
})
|
|
|
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0 && this.isSearchLoading) {
|
|
|
+ Row({ space: 8 }) {
|
|
|
+ LoadingProgress()
|
|
|
+ .width(18)
|
|
|
+ .height(18)
|
|
|
+ .color(this.themeColor)
|
|
|
+ Text('正在构建全局索引,结果会持续补全')
|
|
|
+ .fontSize(12)
|
|
|
+ .fontColor($r('app.color.index_tab_font_color'))
|
|
|
+ .opacity(0.65)
|
|
|
+ }
|
|
|
+ .padding({ left: 20, right: 20, top: this.topSafeHeight + 74, bottom: 6 })
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
// 文件列表(文件夹 + 歌曲)
|
|
|
if (this.webDavFiles.length > 0 || this.songs.length > 0) {
|
|
|
@@ -2734,11 +2842,13 @@ export struct WebDavMainPage {
|
|
|
.margin({ top: 4 })
|
|
|
} else if (!this.isLoading) {
|
|
|
Column() {
|
|
|
- Text('暂无内容')
|
|
|
+ Text(this.isSearchMode && this.searchText.length > 0 ? '没有找到匹配结果' : '暂无内容')
|
|
|
.fontSize(14)
|
|
|
.fontColor($r('app.color.index_tab_font_color'))
|
|
|
.opacity(0.6)
|
|
|
- Text('点击"左侧菜单"网盘加载')
|
|
|
+ Text(this.isSearchMode && this.searchText.length > 0 ?
|
|
|
+ (this.isSearchLoading ? '正在继续扫描更多目录...' : '可以换个关键词再试') :
|
|
|
+ '点击"左侧菜单"网盘加载')
|
|
|
.fontSize(12)
|
|
|
.fontColor($r('app.color.index_tab_font_color'))
|
|
|
.opacity(0.4)
|
|
|
@@ -2851,6 +2961,14 @@ export struct WebDavMainPage {
|
|
|
.maxLines(1)
|
|
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
|
+ Text(this.getFolderSearchLocation(folder))
|
|
|
+ .fontSize(12)
|
|
|
+ .fontColor($r('app.color.index_tab_font_color'))
|
|
|
+ .opacity(0.48)
|
|
|
+ .maxLines(1)
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
+ .visibility(this.getFolderSearchLocation(folder).length > 0 ? Visibility.Visible : Visibility.None)
|
|
|
+
|
|
|
Text('文件夹')
|
|
|
.fontSize(13)
|
|
|
.fontColor($r('app.color.index_tab_font_color'))
|
|
|
@@ -2867,6 +2985,11 @@ export struct WebDavMainPage {
|
|
|
// .backgroundColor($r('app.color.start_window_background'))
|
|
|
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
|
|
|
.onClick(() => {
|
|
|
+ if (this.isSearchMode && this.searchText.length > 0) {
|
|
|
+ this.isSearchMode = false
|
|
|
+ this.searchController.stopEditing()
|
|
|
+ this.onSearchInput('')
|
|
|
+ }
|
|
|
this.enterFolder(folder);
|
|
|
this.breadcrumbs = this.webdavManager.getBreadcrumbs();
|
|
|
})
|
|
|
@@ -2927,6 +3050,14 @@ export struct WebDavMainPage {
|
|
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
}
|
|
|
.width('90%')
|
|
|
+
|
|
|
+ Text(this.getSongSearchLocation(song))
|
|
|
+ .fontSize(12)
|
|
|
+ .fontColor(this.currentSong?.filePath==song.filePath?this.themeColor:$r('app.color.index_tab_font_color'))
|
|
|
+ .opacity(0.42)
|
|
|
+ .maxLines(1)
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
+ .visibility(this.getSongSearchLocation(song).length > 0 ? Visibility.Visible : Visibility.None)
|
|
|
}
|
|
|
.alignItems(HorizontalAlign.Start)
|
|
|
.layoutWeight(1)
|