refactor: separate scraping task tab

This commit is contained in:
hectorqin committed 2026-09-29 14:19:15 +08:00
1 parent 26265e4aac
commit a5d7ee5db8
2 files changed
+8 -6

No files matched your search

+7 -5
View File
@@ -120,7 +120,7 @@ export class MediaScreen {
private favorite=false;
private managing=false;
private managerTab:'libraries'|'tasks'='libraries';
private taskTab:'media'|'ai'='media';
private taskTab:'media'|'ai'|'scrape'='media';
private jobs:ScanJob[]=[];
private aiJobs:AiScanJob[]=[];
private jobLibraryId='';
@@ -511,7 +511,7 @@ export class MediaScreen {
<p className="media-manager-note">扫描只读取原始目录。封面、索引和刮削缓存保存在应用数据目录。</p>
<h2>接入方式</h2><div className="media-connection-row"><Folder size={20} aria-hidden="true"/><span>服务器目录<small>包括服务器已挂载的 NAS</small></span><small>可用</small></div>
</>:<>
<nav className="media-task-tabs" aria-label="扫描任务类型"><button aria-current={this.taskTab==='media'?'page':undefined} onClick={()=>{this.taskTab='media';this.draw();}}>媒体库扫描</button><button aria-current={this.taskTab==='ai'?'page':undefined} onClick={()=>{this.taskTab='ai';this.draw();}}>AI 扫描</button></nav>
<nav className="media-task-tabs" aria-label="任务类型"><button aria-current={this.taskTab==='media'?'page':undefined} onClick={()=>{this.taskTab='media';this.draw();}}>媒体库扫描</button><button aria-current={this.taskTab==='ai'?'page':undefined} onClick={()=>{this.taskTab='ai';this.draw();}}>AI 扫描</button><button aria-current={this.taskTab==='scrape'?'page':undefined} onClick={()=>{this.taskTab='scrape';this.draw();}}>刮削</button></nav>
{this.taskTab==='media'?<>
<div className="media-manager-toolbar"><span>扫描所有媒体库并建立索引</span><button disabled={this.busy||!this.allLibraries.length||this.jobState!=='ready'} onClick={()=>void this.scanAll()}>扫描所有媒体库</button></div>{this.scanNotice&&<p className="media-manager-note" role="status">{this.scanNotice}</p>}
{this.scanActionError&&<div className="media-error" role="alert"><p>{this.scanActionError}</p><button disabled={this.busy} onClick={()=>void this.run(async()=>{await this.loadJobs(this.jobLibraryId);this.scanActionError='';})}>重新核对任务</button></div>}
@@ -519,13 +519,15 @@ export class MediaScreen {
<h2 className="media-task-section-title">媒体库扫描历史{this.jobLibraryId?' · '+this.allLibraries.find(l=>l.id===this.jobLibraryId)?.name:''}</h2>
{this.jobState==='loading'&&!this.busy&&<FloatingNotice message="正在读取扫描任务…" busy/>}{this.jobState==='error'&&<div className="media-error" role="alert"><p>{this.jobError}</p><button disabled={this.busy} onClick={()=>void this.run(()=>this.loadJobs(this.jobLibraryId))}>重试读取扫描任务</button></div>}{this.jobState==='ready'&&this.jobs.length===0&&<p>暂无媒体库扫描历史。</p>}
<ScanJobs key={this.jobLibraryId} jobs={this.jobs} libraries={this.allLibraries} libraryName={this.allLibraries.find(l=>l.id===this.jobLibraryId)?.name||'媒体库'} busy={this.busy||this.jobState!=='ready'} onRetry={id=>void this.scan(id||this.jobLibraryId)} onCancel={id=>void this.run(async()=>{await this.api.request('jobs/'+id+'/cancel','POST');await this.loadJobs(this.jobLibraryId);})} onDelete={id=>void this.run(async()=>{await this.api.request('scan-jobs/'+id,'DELETE');await this.loadJobs(this.jobLibraryId);})}/>
</>:<>
</>:this.taskTab==='ai'?<>
<div className="media-manager-toolbar"><span>AI 识别媒体类型</span><button disabled={this.busy||!this.jobLibraryId} onClick={()=>void this.startAiScan(this.jobLibraryId)}>AI 扫描当前库</button><button disabled={this.busy||!this.allLibraries.length} onClick={()=>void this.startAiScan()}>AI 扫描所有媒体库</button><button disabled={this.busy} onClick={()=>void this.loadAiJobs()} aria-label="刷新 AI 扫描任务"><RefreshCw size={16} aria-hidden="true"/></button></div>{this.scanNotice&&<p className="media-manager-note" role="status">{this.scanNotice}</p>}
<div className="media-task-library"><label>媒体库<MediaSelect aria-label="AI 扫描媒体库" value={this.jobLibraryId} disabled={this.busy||!this.allLibraries.length} onChange={event=>{this.jobLibraryId=event.currentTarget.value;void this.loadAiJobs();}}><option value="">全部媒体库</option>{this.allLibraries.map(l=><option key={l.id} value={l.id}>{l.name}</option>)}</MediaSelect></label></div>
<h2 className="media-task-section-title">AI 扫描历史</h2><AiScanJobs api={this.api} jobs={this.aiJobs} libraries={this.allLibraries} busy={this.busy} onRefresh={()=>void this.loadAiJobs()} onDelete={id=>void this.run(async()=>{await this.api.deleteAiScanJob(id);await this.loadAiJobs();})}/>
</>:<>
<div className="media-manager-toolbar"><span>在线匹配媒体信息</span></div>
<div className="media-manager-scraping"><ScrapeJobs api={this.api} libraries={this.allLibraries} navigate={this.navigate}/></div>
</>}
<p className="media-manager-note">目录不可访问时保留原有资料。扫描与在线匹配分开执行,匹配失败不影响本地播放。</p>
<div className="media-manager-scraping"><ScrapeJobs api={this.api} libraries={this.allLibraries} navigate={this.navigate}/></div>
{this.taskTab!=='scrape'&&<p className="media-manager-note">目录不可访问时保留原有资料。扫描与在线匹配分开执行,匹配失败不影响本地播放。</p>}
</>}
</section>;}
private async permissions(lib:Library){if(this.routing?.navigateRoute){this.openRoute('library-permissions',{library:lib.id});return;}this.permissionLibrary=lib;this.draw();}
+1 -1
View File
@@ -267,7 +267,7 @@
.media-screen .media-browse-actions>button,.media-screen .media-browse-filters>summary {width:44px;min-width:44px;height:44px;padding:12px;box-sizing:border-box;border-radius:50%}
.media-screen .media-detail-favorite {background:transparent;border-radius:50%}
.media-ai-jobs{margin-top:28px;border-top:1px solid var(--media-line,#ddd);padding-top:18px}.media-ai-jobs-heading,.media-ai-job-head,.media-ai-job-actions{display:flex;align-items:center;justify-content:space-between;gap:12px}.media-ai-job{padding:14px 0;border-bottom:1px solid var(--media-line,#ddd)}.media-ai-job-head{align-items:flex-start}.media-ai-job-head small{display:block;margin-top:5px;color:var(--media-muted,#777)}.media-ai-job-actions{justify-content:flex-start;margin-top:10px}.media-ai-batches{margin-top:10px;padding-left:10px}.media-ai-batch{border-top:1px solid var(--media-line,#ddd);padding:8px 0}.media-ai-batch-toggle{display:flex;align-items:center;gap:5px;background:none;border:0;padding:4px;color:inherit}.media-ai-batch-detail{padding:8px 20px;font-size:13px}.media-ai-batch-detail pre{max-height:360px;overflow:auto;white-space:pre-wrap;word-break:break-word;background:rgba(0,0,0,.04);padding:10px}
.media-task-tabs{display:flex;gap:4px;border-bottom:1px solid var(--media-line,#ddd);margin:8px 0 24px}.media-task-tabs button{border:0;border-bottom:2px solid transparent;background:transparent;padding:12px 20px;color:var(--media-muted,#777);font-size:15px}.media-task-tabs button[aria-current=page]{border-bottom-color:currentColor;color:var(--media-ink,#263329);font-weight:600}.media-task-actions{display:flex;gap:6px}.media-task-actions .media-task-icon{position:static}
.media-task-tabs{display:inline-flex;align-items:center;gap:3px;margin:8px 0 28px;padding:4px;border:1px solid var(--border);border-radius:12px;background:var(--surface-muted,#f3f4ef);box-shadow:inset 0 1px 2px rgba(31,42,34,.06)}.media-task-tabs button{min-height:36px;border:0;border-radius:9px;background:transparent;padding:0 18px;color:var(--muted);font-size:14px;font-weight:500;white-space:nowrap;transition:background .15s ease,color .15s ease,box-shadow .15s ease}.media-task-tabs button:hover{color:var(--text);background:rgba(255,255,255,.65)}.media-task-tabs button[aria-current=page]{background:var(--surface,#fff);color:var(--text);font-weight:650;box-shadow:0 1px 3px rgba(31,42,34,.14)}.media-task-actions{display:flex;gap:6px}.media-task-actions .media-task-icon{position:static}
.media-screen .media-detail-page {padding-top:20px}
.media-screen .media-detail-description {margin:16px 0 10px}
.media-detail-description h2 {display:flex;align-items:center;min-height:44px;margin:0 0 8px}