feat(web): 存储设置与文件管理的 Alist 存储切换

- 设置 → 媒体库与目录 → 存储:登记 Alist / OpenList(令牌 / 账号密码 / 访客、路径密码、限速与监控选项),
  测试连接显示版本、用户与容量;媒体库目录可选存储
- 文件管理左栏多一组「Alist 存储」,路径显示「存储名:/路径」;移动 / 复制目标可选 Alist 存储;
  上传按钮;Alist 根点刷新才让 Alist 刷新目录缓存
- 运行状态的挂载根列出存储实例(令牌失效单独提示)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-06 23:57:33 +08:00
1 parent c1bc389058
commit cf364435eb
18 files changed
+618 -41

No files matched your search

+10 -3
View File
@@ -5,6 +5,7 @@ import {
ArrowUp,
ArrowUpAZ,
ClipboardCopy,
Cloud,
Copy,
Download,
Eye,
@@ -47,6 +48,7 @@ import {
isRootOffline,
type ListQuery,
refKey,
refreshRemoteDir,
useFileList,
useFileRoots,
useProbe,
@@ -64,11 +66,13 @@ import {
RenameDialog,
ScrapeDialog,
TransferDialog,
UploadButton,
} from "~/components/files/dialogs.tsx";
import { DirTree } from "~/components/files/dir-tree.tsx";
import { FileDetail } from "~/components/files/file-detail.tsx";
import { DRAG_MIME, FileList } from "~/components/files/file-list.tsx";
import {
displayPath,
emptySelection,
FILTER_CHIPS,
groupRoots,
@@ -221,7 +225,7 @@ function TreeRail({
<span aria-hidden className="my-1 h-px w-6 bg-border" />
{groupRoots(roots).flatMap((g) =>
g.items.map((r) => {
const Icon = r.kind === "download" ? Download : HardDrive;
const Icon = r.kind === "download" ? Download : r.kind === "storage" ? Cloud : HardDrive;
return (
<button
key={r.id}
@@ -429,7 +433,7 @@ export default function FilesPage() {
const copyPaths = async (list: FileEntry[]) => {
if (!root) return;
const text = list.map((e) => `${root.hostPath}${e.path ? `/${e.path}` : ""}`).join("\n");
const text = list.map((e) => displayPath(root, e.path)).join("\n");
try {
await navigator.clipboard.writeText(text);
toast.success(`已复制 ${list.length} 个路径`);
@@ -1053,7 +1057,9 @@ export default function FilesPage() {
size="icon-sm"
aria-label="刷新"
title="刷新"
onClick={() => {
onClick={async () => {
// Alist:手动刷新才让 Alist 刷新目录缓存(穿透到网盘接口)
if (root?.storage) await refreshRemoteDir(root.id, path).catch(() => undefined);
void roots.refetch();
void list.refetch();
}}
@@ -1088,6 +1094,7 @@ export default function FilesPage() {
新建目录
</Button>
</span>
{root ? <UploadButton root={root} path={path} /> : null}
</div>
<div className="flex flex-wrap items-center gap-2">
{searchBox}