metube/ui/src/app/library-browser.component.html

126 lines
7.6 KiB
HTML

<section class="list-section">
<div class="section-head">
<h2 class="section-title">媒体库</h2>
<span class="section-meta num">{{ folderLabel }} · {{ files.length }} 项</span>
</div>
<div class="list-toolbar">
<div class="filter-wrap">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="search" placeholder="筛选文件名…" aria-label="筛选文件名"
[(ngModel)]="filter">
</div>
<select class="tool-select" aria-label="排序方式" [(ngModel)]="sort">
<option value="name">按名称</option>
<option value="date">按时间</option>
<option value="size">按大小</option>
</select>
<button class="sort-dir" (click)="sortAscending = !sortAscending" [title]="sortAscending ? '升序,点击切换' : '降序,点击切换'">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline [attr.points]="sortAscending ? '6 15 12 9 18 15' : '6 9 12 15 18 9'"/></svg>
</button>
<div class="view-toggle" aria-label="视图切换">
<button type="button" [class.on]="view === 'list'" (click)="view = 'list'" title="列表视图" aria-label="列表视图">
<fa-icon [icon]="faList"></fa-icon>
</button>
<button type="button" [class.on]="view === 'grid'" (click)="view = 'grid'" title="网格视图" aria-label="网格视图">
<fa-icon [icon]="faThLarge"></fa-icon>
</button>
</div>
<button class="ghost-btn" (click)="toggleSelectAll()">{{ allSelected() ? '取消全选' : '全选' }}</button>
<button class="ghost-btn" (click)="refresh.emit()" title="刷新">
<fa-icon [icon]="faRedoAlt"></fa-icon>&nbsp;刷新
</button>
<span class="result-count num">显示 {{ filteredFiles.length }} / {{ files.length }}</span>
</div>
<div class="empty" *ngIf="!loading && files.length === 0">
<svg width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><polyline points="10 9 15 12 10 15"/></svg>
<div class="empty-title">此文件夹为空</div>
<div class="empty-sub">从「已完成」整理文件到媒体库后会出现在这里</div>
</div>
<div class="list-frame" *ngIf="files.length > 0">
<div class="lib-rows" *ngIf="view === 'list'">
<div class="lib-row" *ngFor="let file of filteredFiles; trackBy: trackByName" [class.selected]="isSelected(filePath(file))">
<input type="checkbox" class="lib-check" [checked]="isSelected(filePath(file))" (change)="toggleSelect(filePath(file), $event.target.checked)" [attr.aria-label]="'选择 ' + file.name">
<div class="thumb clickable" (click)="play.emit(file)">
<img [src]="thumbUrl(file)" [alt]="file.name" loading="lazy">
</div>
<div class="lib-main">
<div class="lib-title clickable" [title]="file.name" (click)="play.emit(file)">{{ file.name }}</div>
<div class="lib-meta">
<span *ngIf="file.meta?.website" class="lib-badge site" [title]="file.meta?.website">{{ siteLabel(file.meta?.website) }}</span>
<span class="num">{{ file.size | fileSize }}</span>
<span>{{ fileTime(file) }}</span>
</div>
</div>
<div class="lib-actions">
<a *ngIf="file.meta?.url" class="icon-btn neutral" [href]="file.meta?.url" target="_blank" rel="noopener noreferrer" title="打开原始页面" aria-label="打开原始页面">
<fa-icon [icon]="faExternalLinkAlt"></fa-icon>
</a>
<button type="button" class="icon-btn neutral" (click)="play.emit(file)" title="在线播放" aria-label="在线播放">
<fa-icon [icon]="faPlay"></fa-icon>
</button>
<button type="button" class="icon-btn" (click)="del.emit(file)" title="删除" aria-label="删除">
<fa-icon [icon]="faTrashAlt"></fa-icon>
</button>
</div>
</div>
</div>
<div class="grid-wrap" *ngIf="view === 'grid'">
<div class="grid">
<div class="card" *ngFor="let file of filteredFiles; trackBy: trackByName" [class.selected]="isSelected(filePath(file))">
<input type="checkbox" class="card-check" [checked]="isSelected(filePath(file))" (change)="toggleSelect(filePath(file), $event.target.checked)" [attr.aria-label]="'选择 ' + file.name">
<div class="thumb clickable" (click)="play.emit(file)">
<img [src]="thumbUrl(file)" [alt]="file.name" loading="lazy">
</div>
<div class="card-body">
<div class="card-title clickable" [title]="file.name" (click)="play.emit(file)">{{ file.name }}</div>
<div class="card-meta">
<span *ngIf="file.meta?.website" class="lib-badge site" [title]="file.meta?.website">{{ siteLabel(file.meta?.website) }}</span>
<span class="num">{{ file.size | fileSize }}</span>
<span>{{ fileTime(file) }}</span>
</div>
<div class="card-actions">
<a *ngIf="file.meta?.url" class="icon-btn neutral" [href]="file.meta?.url" target="_blank" rel="noopener noreferrer" title="打开原始页面" aria-label="打开原始页面">
<fa-icon [icon]="faExternalLinkAlt"></fa-icon>
</a>
<button type="button" class="icon-btn neutral" (click)="play.emit(file)" title="在线播放" aria-label="在线播放">
<fa-icon [icon]="faPlay"></fa-icon>
</button>
<button type="button" class="icon-btn" (click)="del.emit(file)" title="删除" aria-label="删除">
<fa-icon [icon]="faTrashAlt"></fa-icon>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="batchbar" [class.show]="selected.size > 0">
<span class="batch-count">已选 {{ selected.size }} 项</span>
<div class="move-wrap">
<button type="button" class="batch-btn" (click)="toggleMoveMenu()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
移动到…
</button>
<div class="move-menu" *ngIf="moveMenuOpen">
<input type="text" class="move-search" placeholder="搜索文件夹…" [(ngModel)]="moveQuery" autofocus>
<button *ngFor="let opt of folderOptions()" type="button" class="move-item" (click)="moveTo(opt.path)">{{ opt.label }}</button>
<button *ngIf="!folderOptions().length" type="button" class="move-item" disabled>无匹配文件夹</button>
<div class="move-new">
<input type="text" placeholder="新建文件夹…" [(ngModel)]="newFolder" (keydown.enter)="confirmNewFolder()">
<button type="button" class="batch-btn small" (click)="confirmNewFolder()">创建</button>
</div>
</div>
</div>
<button type="button" class="batch-btn danger" (click)="delSelected()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
删除
</button>
<button type="button" class="batch-btn" (click)="clearSelection()">取消选择</button>
</div>
</section>