feat(web): file manager v2 and shared organize wizard (FE-4)
- 左树可折叠(44px 图标条)、下载目录在前、上一级、初始位置记忆;列表名称副行(作品·季集 / 解析提示 / 目录计数)、状态列 120px - 行内 hover 2 个主操作;右键/⋯ 菜单按 v2 顺序,不适用隐藏、被阻止灰显带原因;批量栏"更多" - 详情抽屉:识别节(解析 + Top5 候选 + 按此整理)、整理记录节、版本目标、文本预览前 256 KB - 共享整理向导 components/organize-wizard:3 步(文件与作品表格/批量条 → 选项 → 预览并执行), TMDB 候选 external 就地建作品、最小体积、onExisting、命名来源、入库后刷新元数据、只看问题、分段着色、 plan_stale 自动重新预览、执行进度回查 /files/records;来源支持文件/下载任务/整理记录 - 重命名默认值取 suggest-name;批量刷新元数据走 scrape-batch;回收站条数、原位置链接、占用后改"还原到…" - 快捷键 Alt+↑、Ctrl/⌘+Enter、?;移动端单栏工具栏与底部批量栏 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
09a6b0b5d3
commit
c31df8b69a
24 files changed
+4820
-1607
No files matched your search
@@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
|
||||
import { type FileRoot, useDirChildren } from "~/api/files.ts";
|
||||
import { healthLabel } from "~/lib/labels.ts";
|
||||
import { cn, formatBytes } from "~/lib/utils.ts";
|
||||
import { joinRel, segments } from "./model.ts";
|
||||
import { groupRoots, joinRel, segments } from "./model.ts";
|
||||
|
||||
type Loc = { root: string; path: string };
|
||||
type DropHandler = (target: Loc, e: React.DragEvent) => void;
|
||||
@@ -11,7 +11,7 @@ type DropHandler = (target: Loc, e: React.DragEvent) => void;
|
||||
const nodeKey = (root: string, path: string) => `${root}\u0000${path}`;
|
||||
|
||||
/**
|
||||
* 根列表 + 懒加载目录树。按 库 / 下载 分组,每根带健康徽标与剩余空间;
|
||||
* 登记目录 + 懒加载目录树。下载目录在前、媒体库在后,每个登记目录带健康点与剩余空间;
|
||||
* 目录节点可作为拖放目标(onDropTo),并自动展开到当前路径。
|
||||
* pickOnly:目标选择器模式(移动/复制到…),不可写根整根禁用。
|
||||
*/
|
||||
@@ -55,13 +55,10 @@ export function DirTree({
|
||||
return next;
|
||||
});
|
||||
|
||||
const groups = [
|
||||
{ title: "媒体库", items: roots.filter((r) => r.kind === "library") },
|
||||
{ title: "下载目录", items: roots.filter((r) => r.kind === "download") },
|
||||
].filter((g) => g.items.length > 0);
|
||||
const groups = groupRoots(roots);
|
||||
|
||||
return (
|
||||
<nav aria-label="根与目录" className={cn("space-y-4", className)}>
|
||||
<nav aria-label="登记目录" className={cn("space-y-4", className)}>
|
||||
{groups.map((g) => (
|
||||
<section key={g.title} aria-label={g.title}>
|
||||
<p className="mb-1 px-2 text-micro font-semibold tracking-[0.15em] text-muted-foreground uppercase">
|
||||
@@ -126,7 +123,7 @@ function RootNode({
|
||||
if (!open.has(k)) toggle(k);
|
||||
}}
|
||||
onDrop={onDropTo && root.writable && !offline ? (e) => onDropTo({ root: root.id, path: "" }, e) : undefined}
|
||||
title={disabled ? (offline ? "根离线" : "只读根,不能作为目标") : root.hostPath}
|
||||
title={disabled ? (offline ? "目录离线" : "只读目录,不能作为目标") : root.hostPath}
|
||||
>
|
||||
<Icon className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
<span className="min-w-0 flex-1">
|
||||
@@ -150,7 +147,7 @@ function RootNode({
|
||||
</span>
|
||||
)}
|
||||
{root.usage ? <span className="truncate">剩余 {formatBytes(root.usage.freeBytes, 1)}</span> : null}
|
||||
{!root.writable ? <span>只读</span> : null}
|
||||
{!root.writable && root.kind === "library" ? <span>只读</span> : null}
|
||||
</span>
|
||||
</span>
|
||||
</TreeRow>
|
||||
@@ -229,8 +226,8 @@ function Children({
|
||||
depth={depth}
|
||||
active={active}
|
||||
disabled={disabled}
|
||||
expandable
|
||||
expanded={isOpen}
|
||||
expandable={d.hasChildren}
|
||||
expanded={isOpen && d.hasChildren}
|
||||
onToggle={() => toggle(k)}
|
||||
onClick={() => !disabled && onSelect({ root: root.id, path: p })}
|
||||
onDrop={onDropTo && root.writable ? (e) => onDropTo({ root: root.id, path: p }, e) : undefined}
|
||||
@@ -243,7 +240,7 @@ function Children({
|
||||
)}
|
||||
<span className="min-w-0 flex-1 truncate text-body">{d.name}</span>
|
||||
</TreeRow>
|
||||
{isOpen ? (
|
||||
{isOpen && d.hasChildren ? (
|
||||
<Children
|
||||
root={root}
|
||||
path={p}
|
||||
@@ -261,7 +258,7 @@ function Children({
|
||||
})}
|
||||
{q.data?.truncated ? (
|
||||
<p className="py-1 text-meta text-muted-foreground" style={{ paddingLeft: depth * 14 + 28 }}>
|
||||
只显示前 1000 个子目录
|
||||
子目录过多,只列出一部分
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user