vitepress-plugin-filelist 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/client/browser/index.d.ts +17 -1
- package/dist/client/browser/index.js +109 -44
- package/dist/client/ssr/index.d.ts +17 -1
- package/dist/client/ssr/index.js +84 -6
- package/dist/client/style.css +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,8 @@ export default {
|
|
|
73
73
|
| --- | --- | --- | --- |
|
|
74
74
|
| `base` | `string` | `''` | OpenList 站点地址;为空时进入演示模式 |
|
|
75
75
|
| `path` | `string` | `'/'` | 初始目录 |
|
|
76
|
+
| `sources` | `FileListSource[]` | `[]` | 多根挂载;设置后忽略 `path` |
|
|
77
|
+
| `expanded` | `boolean` | `false` | 目录根是否默认展开 |
|
|
76
78
|
| `token` | `string` | `''` | 可选只读 token;不传则以游客身份请求 |
|
|
77
79
|
| `password` | `string` | `''` | 目录密码,也可以在运行时输入 |
|
|
78
80
|
| `title` | `string` | `''` | 工具栏标题;为空时回退到 OpenList 的 `site_title` |
|
|
@@ -81,6 +83,35 @@ export default {
|
|
|
81
83
|
|
|
82
84
|
同时注册了 `<FileList>` 和 `<VPFileList>` 两个组件名。
|
|
83
85
|
|
|
86
|
+
## 多根挂载
|
|
87
|
+
|
|
88
|
+
```md
|
|
89
|
+
<FileList
|
|
90
|
+
base="https://pan.example.com"
|
|
91
|
+
title="课程资料"
|
|
92
|
+
:sources="[
|
|
93
|
+
{ path: '/课程资料/语文' },
|
|
94
|
+
{ path: '/课程资料/数学', expanded: true },
|
|
95
|
+
{ path: '/课程资料/说明.pdf' }
|
|
96
|
+
]"
|
|
97
|
+
/>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- 目录路径挂载为根文件夹,默认折叠;`expanded` 可为全局或单个 source 打开
|
|
101
|
+
- 文件路径挂载为一条文件记录,直接预览/下载
|
|
102
|
+
- 根目录复选框与勾选、全选效果一致:选中根目录会选中其已加载的子项
|
|
103
|
+
- 每个 source 可单独设置 `title`、`password`
|
|
104
|
+
- 根节点顺序按 `sources` 顺序,子项仍按名称/大小/日期排序
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
interface FileListSource {
|
|
108
|
+
path: string
|
|
109
|
+
title?: string
|
|
110
|
+
password?: string
|
|
111
|
+
expanded?: boolean
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
84
115
|
## 国际化
|
|
85
116
|
|
|
86
117
|
```md
|
|
@@ -34,11 +34,25 @@ interface FileListLabels {
|
|
|
34
34
|
batchFailed: string;
|
|
35
35
|
defaultZip: string;
|
|
36
36
|
}
|
|
37
|
+
interface FileListSource {
|
|
38
|
+
/** OpenList path, directory or single file. */
|
|
39
|
+
path: string;
|
|
40
|
+
/** Root label. Defaults to the last path segment. */
|
|
41
|
+
title?: string;
|
|
42
|
+
/** Per-source folder password. */
|
|
43
|
+
password?: string;
|
|
44
|
+
/** Initial expanded state for a directory source. Overrides the global `expanded` prop. */
|
|
45
|
+
expanded?: boolean;
|
|
46
|
+
}
|
|
37
47
|
interface FileListProps {
|
|
38
48
|
/** OpenList base URL, e.g. `https://pan.example.com`. Empty string enables demo mode. */
|
|
39
49
|
base?: string;
|
|
40
50
|
/** Initial directory, defaults to `/`. */
|
|
41
51
|
path?: string;
|
|
52
|
+
/** Mount multiple paths as root nodes. When set, `path` is ignored. */
|
|
53
|
+
sources?: FileListSource[];
|
|
54
|
+
/** Expand directory roots initially. Defaults to `false`. */
|
|
55
|
+
expanded?: boolean;
|
|
42
56
|
/** Optional read-only token. Guest access is used when omitted. */
|
|
43
57
|
token?: string;
|
|
44
58
|
/** Folder password. The component also asks at runtime when OpenList reports one. */
|
|
@@ -64,6 +78,8 @@ interface OpenListEntry {
|
|
|
64
78
|
declare const _default: import("vue").DefineComponent<FileListProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FileListProps> & Readonly<{}>, {
|
|
65
79
|
base: string;
|
|
66
80
|
path: string;
|
|
81
|
+
sources: FileListSource[];
|
|
82
|
+
expanded: boolean;
|
|
67
83
|
token: string;
|
|
68
84
|
password: string;
|
|
69
85
|
title: string;
|
|
@@ -176,4 +192,4 @@ export declare function compareNodes(a: SortableNode, b: SortableNode, key: Sort
|
|
|
176
192
|
* ```
|
|
177
193
|
*/
|
|
178
194
|
export declare function enhanceAppWithFileList({ app }: EnhanceAppContext): void;
|
|
179
|
-
export { _default as FileList, _default as VPFileList, type FileListLabels, type FileListLocale, type FileListProps, type OpenListEntry };
|
|
195
|
+
export { _default as FileList, _default as VPFileList, type FileListLabels, type FileListLocale, type FileListProps, type FileListSource, type OpenListEntry };
|
|
@@ -173,32 +173,33 @@ const _hoisted_23 = [
|
|
|
173
173
|
".indeterminate",
|
|
174
174
|
"onChange"
|
|
175
175
|
];
|
|
176
|
-
const _hoisted_24 = ["
|
|
177
|
-
const _hoisted_25 =
|
|
176
|
+
const _hoisted_24 = ["title"];
|
|
177
|
+
const _hoisted_25 = ["onClick"];
|
|
178
|
+
const _hoisted_26 = {
|
|
178
179
|
key: 1,
|
|
179
180
|
class: "fl-arrow-placeholder"
|
|
180
181
|
};
|
|
181
|
-
const
|
|
182
|
-
const
|
|
182
|
+
const _hoisted_27 = ["onClick"];
|
|
183
|
+
const _hoisted_28 = {
|
|
183
184
|
key: 0,
|
|
184
185
|
class: "fl-folder-icon"
|
|
185
186
|
};
|
|
186
|
-
const
|
|
187
|
+
const _hoisted_29 = {
|
|
187
188
|
key: 1,
|
|
188
189
|
class: "fl-ext-badge"
|
|
189
190
|
};
|
|
190
|
-
const
|
|
191
|
+
const _hoisted_30 = {
|
|
191
192
|
key: 2,
|
|
192
193
|
class: "fl-loading"
|
|
193
194
|
};
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
const _hoisted_33 = ["title", "onClick"];
|
|
195
|
+
const _hoisted_31 = { class: "fl-cell fl-cell-size" };
|
|
196
|
+
const _hoisted_32 = { class: "fl-cell fl-cell-date" };
|
|
197
|
+
const _hoisted_33 = { class: "fl-cell fl-cell-op" };
|
|
198
198
|
const _hoisted_34 = ["title", "onClick"];
|
|
199
|
-
const _hoisted_35 =
|
|
200
|
-
const _hoisted_36 = {
|
|
201
|
-
const _hoisted_37 = {
|
|
199
|
+
const _hoisted_35 = ["title", "onClick"];
|
|
200
|
+
const _hoisted_36 = { class: "fl-foot" };
|
|
201
|
+
const _hoisted_37 = { key: 0 };
|
|
202
|
+
const _hoisted_38 = {
|
|
202
203
|
key: 4,
|
|
203
204
|
class: "fl-toast"
|
|
204
205
|
};
|
|
@@ -207,6 +208,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
207
208
|
props: {
|
|
208
209
|
base: { default: "" },
|
|
209
210
|
path: { default: "/" },
|
|
211
|
+
sources: { default: () => [] },
|
|
212
|
+
expanded: {
|
|
213
|
+
type: Boolean,
|
|
214
|
+
default: false
|
|
215
|
+
},
|
|
210
216
|
token: { default: "" },
|
|
211
217
|
password: { default: "" },
|
|
212
218
|
title: { default: "" },
|
|
@@ -409,14 +415,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
409
415
|
if (payload.code !== 200) throw new ApiError(payload.message || `HTTP ${response.status}`, payload.code ?? response.status);
|
|
410
416
|
return payload.data;
|
|
411
417
|
}
|
|
412
|
-
async function listDir(path) {
|
|
418
|
+
async function listDir(path, password = activePassword.value) {
|
|
413
419
|
if (demo.value) {
|
|
414
420
|
await new Promise((resolve) => setTimeout(resolve, 220));
|
|
415
421
|
return DEMO_TREE[path] ?? [];
|
|
416
422
|
}
|
|
417
423
|
return (await request("/api/fs/list", {
|
|
418
424
|
path,
|
|
419
|
-
password
|
|
425
|
+
password,
|
|
420
426
|
page: 1,
|
|
421
427
|
per_page: 0,
|
|
422
428
|
refresh: false
|
|
@@ -424,13 +430,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
424
430
|
}
|
|
425
431
|
async function resolveUrl(node) {
|
|
426
432
|
if (demo.value) return "";
|
|
433
|
+
if (node.raw_url) return node.raw_url;
|
|
427
434
|
const cached = downloadCache.get(node.path);
|
|
428
435
|
if (cached) return cached;
|
|
429
436
|
let url = "";
|
|
430
437
|
try {
|
|
431
438
|
url = (await request("/api/fs/get", {
|
|
432
439
|
path: node.path,
|
|
433
|
-
password: activePassword.value
|
|
440
|
+
password: node.password || activePassword.value
|
|
434
441
|
}))?.raw_url ?? "";
|
|
435
442
|
} catch {
|
|
436
443
|
url = "";
|
|
@@ -455,7 +462,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
455
462
|
nodes.sort((a, b) => compareNodes(a, b, sortKey.value, sortAsc.value));
|
|
456
463
|
for (const node of nodes) if (node.children.length) sortTree(node.children);
|
|
457
464
|
}
|
|
458
|
-
function toNodes(items, parent, depth) {
|
|
465
|
+
function toNodes(items, parent, depth, password = "") {
|
|
459
466
|
return items.map((item) => ({
|
|
460
467
|
...item,
|
|
461
468
|
path: joinPath(parent, item.name),
|
|
@@ -463,7 +470,8 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
463
470
|
expanded: false,
|
|
464
471
|
loaded: false,
|
|
465
472
|
loading: false,
|
|
466
|
-
children: []
|
|
473
|
+
children: [],
|
|
474
|
+
password
|
|
467
475
|
}));
|
|
468
476
|
}
|
|
469
477
|
function setSort(key) {
|
|
@@ -472,15 +480,82 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
472
480
|
sortKey.value = key;
|
|
473
481
|
sortAsc.value = true;
|
|
474
482
|
}
|
|
475
|
-
|
|
483
|
+
if (props.sources.length) {
|
|
484
|
+
for (const root of roots.value) if (root.children.length) sortTree(root.children);
|
|
485
|
+
} else sortTree(roots.value);
|
|
486
|
+
}
|
|
487
|
+
function sourceName(path) {
|
|
488
|
+
const parts = path.split("/").filter(Boolean);
|
|
489
|
+
return parts[parts.length - 1] || path;
|
|
490
|
+
}
|
|
491
|
+
async function loadChildren(node) {
|
|
492
|
+
if (node.loaded) return true;
|
|
493
|
+
node.loading = true;
|
|
494
|
+
try {
|
|
495
|
+
node.children = toNodes(await listDir(node.path, node.password), node.path, node.depth + 1, node.password);
|
|
496
|
+
sortTree(node.children);
|
|
497
|
+
node.loaded = true;
|
|
498
|
+
return true;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
showToast(t("folderFailed", { message: errorText(error) }));
|
|
501
|
+
return false;
|
|
502
|
+
} finally {
|
|
503
|
+
node.loading = false;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async function loadSourceRoot(source) {
|
|
507
|
+
const path = source.path;
|
|
508
|
+
const password = source.password ?? "";
|
|
509
|
+
let item = {
|
|
510
|
+
name: sourceName(path),
|
|
511
|
+
size: 0,
|
|
512
|
+
is_dir: true,
|
|
513
|
+
modified: ""
|
|
514
|
+
};
|
|
515
|
+
if (demo.value) {
|
|
516
|
+
if (/\/[^/]+\.[^/]+$/.test(path)) item = {
|
|
517
|
+
...item,
|
|
518
|
+
is_dir: false,
|
|
519
|
+
modified: "2026-09-01T08:00:00+08:00"
|
|
520
|
+
};
|
|
521
|
+
} else try {
|
|
522
|
+
const data = await request("/api/fs/get", {
|
|
523
|
+
path,
|
|
524
|
+
password
|
|
525
|
+
});
|
|
526
|
+
item = {
|
|
527
|
+
...item,
|
|
528
|
+
...data
|
|
529
|
+
};
|
|
530
|
+
} catch {}
|
|
531
|
+
return {
|
|
532
|
+
...item,
|
|
533
|
+
name: source.title || item.name || sourceName(path),
|
|
534
|
+
path,
|
|
535
|
+
depth: 0,
|
|
536
|
+
expanded: source.expanded ?? props.expanded,
|
|
537
|
+
loaded: false,
|
|
538
|
+
loading: false,
|
|
539
|
+
children: [],
|
|
540
|
+
password,
|
|
541
|
+
isRoot: true
|
|
542
|
+
};
|
|
476
543
|
}
|
|
477
544
|
async function loadRoot() {
|
|
478
545
|
loadingRoot.value = true;
|
|
479
546
|
errorMsg.value = "";
|
|
480
547
|
needsPassword.value = false;
|
|
481
548
|
try {
|
|
549
|
+
if (props.sources.length) {
|
|
550
|
+
const nodes = await Promise.all(props.sources.map((source) => loadSourceRoot(source)));
|
|
551
|
+
roots.value = nodes;
|
|
552
|
+
for (const node of roots.value) if (node.is_dir && node.expanded) node.expanded = await loadChildren(node);
|
|
553
|
+
selected.value = /* @__PURE__ */ new Set();
|
|
554
|
+
downloadCache.clear();
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
482
557
|
const items = await listDir(props.path);
|
|
483
|
-
roots.value = toNodes(items, props.path, 0);
|
|
558
|
+
roots.value = toNodes(items, props.path, 0, activePassword.value);
|
|
484
559
|
sortTree(roots.value);
|
|
485
560
|
selected.value = /* @__PURE__ */ new Set();
|
|
486
561
|
downloadCache.clear();
|
|
@@ -498,19 +573,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
498
573
|
node.expanded = false;
|
|
499
574
|
return;
|
|
500
575
|
}
|
|
501
|
-
if (!node
|
|
502
|
-
node.loading = true;
|
|
503
|
-
try {
|
|
504
|
-
node.children = toNodes(await listDir(node.path), node.path, node.depth + 1);
|
|
505
|
-
sortTree(node.children);
|
|
506
|
-
node.loaded = true;
|
|
507
|
-
} catch (error) {
|
|
508
|
-
showToast(t("folderFailed", { message: errorText(error) }));
|
|
509
|
-
return;
|
|
510
|
-
} finally {
|
|
511
|
-
node.loading = false;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
576
|
+
if (!await loadChildren(node)) return;
|
|
514
577
|
node.expanded = true;
|
|
515
578
|
}
|
|
516
579
|
const visible = computed(() => {
|
|
@@ -735,6 +798,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
735
798
|
key: node.path,
|
|
736
799
|
class: normalizeClass(["fl-row", {
|
|
737
800
|
"fl-row-dir": node.is_dir,
|
|
801
|
+
"fl-row-root": node.isRoot,
|
|
738
802
|
"fl-row-selected": selected.value.has(node.path)
|
|
739
803
|
}])
|
|
740
804
|
}, [
|
|
@@ -746,23 +810,24 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
746
810
|
}, null, 40, _hoisted_23)]),
|
|
747
811
|
createElementVNode("span", {
|
|
748
812
|
class: "fl-cell fl-cell-name",
|
|
813
|
+
title: node.isRoot ? node.path : void 0,
|
|
749
814
|
style: normalizeStyle({ paddingLeft: `${node.depth * 22}px` })
|
|
750
815
|
}, [node.is_dir ? (openBlock(), createElementBlock("button", {
|
|
751
816
|
key: 0,
|
|
752
817
|
class: normalizeClass(["fl-arrow", { "fl-arrow-open": node.expanded }]),
|
|
753
818
|
type: "button",
|
|
754
819
|
onClick: ($event) => toggleExpand(node)
|
|
755
|
-
}, "▸", 10,
|
|
820
|
+
}, "▸", 10, _hoisted_25)) : (openBlock(), createElementBlock("span", _hoisted_26)), createElementVNode("span", {
|
|
756
821
|
class: normalizeClass(["fl-name", { "fl-name-link": node.is_dir }]),
|
|
757
822
|
onClick: ($event) => node.is_dir ? toggleExpand(node) : download(node)
|
|
758
823
|
}, [
|
|
759
|
-
node.is_dir ? (openBlock(), createElementBlock("span",
|
|
824
|
+
node.is_dir ? (openBlock(), createElementBlock("span", _hoisted_28, "📁")) : (openBlock(), createElementBlock("span", _hoisted_29, toDisplayString(unref(extOf)(node.name) || "FILE"), 1)),
|
|
760
825
|
createTextVNode(" " + toDisplayString(node.name) + " ", 1),
|
|
761
|
-
node.loading ? (openBlock(), createElementBlock("span",
|
|
762
|
-
], 10,
|
|
763
|
-
createElementVNode("span",
|
|
764
|
-
createElementVNode("span",
|
|
765
|
-
createElementVNode("span",
|
|
826
|
+
node.loading ? (openBlock(), createElementBlock("span", _hoisted_30, "…")) : createCommentVNode("v-if", true)
|
|
827
|
+
], 10, _hoisted_27)], 12, _hoisted_24),
|
|
828
|
+
createElementVNode("span", _hoisted_31, toDisplayString(node.is_dir ? "-" : unref(formatSize)(node.size)), 1),
|
|
829
|
+
createElementVNode("span", _hoisted_32, toDisplayString(unref(formatDate)(node.modified)), 1),
|
|
830
|
+
createElementVNode("span", _hoisted_33, [!node.is_dir ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [canPreview(node) ? (openBlock(), createElementBlock("button", {
|
|
766
831
|
key: 0,
|
|
767
832
|
class: "fl-download",
|
|
768
833
|
type: "button",
|
|
@@ -782,7 +847,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
782
847
|
cx: "12",
|
|
783
848
|
cy: "12",
|
|
784
849
|
r: "3"
|
|
785
|
-
})], -1)])], 8,
|
|
850
|
+
})], -1)])], 8, _hoisted_34)) : createCommentVNode("v-if", true), createElementVNode("button", {
|
|
786
851
|
class: "fl-download",
|
|
787
852
|
type: "button",
|
|
788
853
|
title: t("download"),
|
|
@@ -801,11 +866,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
801
866
|
createElementVNode("path", { d: "M12 3v12" }),
|
|
802
867
|
createElementVNode("path", { d: "m7 10 5 5 5-5" }),
|
|
803
868
|
createElementVNode("path", { d: "M5 21h14" })
|
|
804
|
-
], -1)])], 8,
|
|
869
|
+
], -1)])], 8, _hoisted_35)], 64)) : createCommentVNode("v-if", true)])
|
|
805
870
|
], 2);
|
|
806
871
|
}), 128)),
|
|
807
|
-
createElementVNode("div",
|
|
808
|
-
toast.value ? (openBlock(), createElementBlock("div",
|
|
872
|
+
createElementVNode("div", _hoisted_36, [createElementVNode("span", null, toDisplayString(t("loaded", { count: allFiles.value.length })) + " · " + toDisplayString(t("selected", { count: selected.value.size })), 1), unloadedFolders.value ? (openBlock(), createElementBlock("span", _hoisted_37, " · " + toDisplayString(t("unloaded", { count: unloadedFolders.value })), 1)) : createCommentVNode("v-if", true)]),
|
|
873
|
+
toast.value ? (openBlock(), createElementBlock("div", _hoisted_38, toDisplayString(toast.value), 1)) : createCommentVNode("v-if", true)
|
|
809
874
|
]);
|
|
810
875
|
};
|
|
811
876
|
}
|
|
@@ -34,11 +34,25 @@ interface FileListLabels {
|
|
|
34
34
|
batchFailed: string;
|
|
35
35
|
defaultZip: string;
|
|
36
36
|
}
|
|
37
|
+
interface FileListSource {
|
|
38
|
+
/** OpenList path, directory or single file. */
|
|
39
|
+
path: string;
|
|
40
|
+
/** Root label. Defaults to the last path segment. */
|
|
41
|
+
title?: string;
|
|
42
|
+
/** Per-source folder password. */
|
|
43
|
+
password?: string;
|
|
44
|
+
/** Initial expanded state for a directory source. Overrides the global `expanded` prop. */
|
|
45
|
+
expanded?: boolean;
|
|
46
|
+
}
|
|
37
47
|
interface FileListProps {
|
|
38
48
|
/** OpenList base URL, e.g. `https://pan.example.com`. Empty string enables demo mode. */
|
|
39
49
|
base?: string;
|
|
40
50
|
/** Initial directory, defaults to `/`. */
|
|
41
51
|
path?: string;
|
|
52
|
+
/** Mount multiple paths as root nodes. When set, `path` is ignored. */
|
|
53
|
+
sources?: FileListSource[];
|
|
54
|
+
/** Expand directory roots initially. Defaults to `false`. */
|
|
55
|
+
expanded?: boolean;
|
|
42
56
|
/** Optional read-only token. Guest access is used when omitted. */
|
|
43
57
|
token?: string;
|
|
44
58
|
/** Folder password. The component also asks at runtime when OpenList reports one. */
|
|
@@ -64,6 +78,8 @@ interface OpenListEntry {
|
|
|
64
78
|
declare const _default: import("vue").DefineComponent<FileListProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FileListProps> & Readonly<{}>, {
|
|
65
79
|
base: string;
|
|
66
80
|
path: string;
|
|
81
|
+
sources: FileListSource[];
|
|
82
|
+
expanded: boolean;
|
|
67
83
|
token: string;
|
|
68
84
|
password: string;
|
|
69
85
|
title: string;
|
|
@@ -176,4 +192,4 @@ export declare function compareNodes(a: SortableNode, b: SortableNode, key: Sort
|
|
|
176
192
|
* ```
|
|
177
193
|
*/
|
|
178
194
|
export declare function enhanceAppWithFileList({ app }: EnhanceAppContext): void;
|
|
179
|
-
export { _default as FileList, _default as VPFileList, type FileListLabels, type FileListLocale, type FileListProps, type OpenListEntry };
|
|
195
|
+
export { _default as FileList, _default as VPFileList, type FileListLabels, type FileListLocale, type FileListProps, type FileListSource, type OpenListEntry };
|
package/dist/client/ssr/index.js
CHANGED
|
@@ -131,6 +131,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
131
131
|
props: {
|
|
132
132
|
base: { default: "" },
|
|
133
133
|
path: { default: "/" },
|
|
134
|
+
sources: { default: () => [] },
|
|
135
|
+
expanded: {
|
|
136
|
+
type: Boolean,
|
|
137
|
+
default: false
|
|
138
|
+
},
|
|
134
139
|
token: { default: "" },
|
|
135
140
|
password: { default: "" },
|
|
136
141
|
title: { default: "" },
|
|
@@ -167,6 +172,12 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
167
172
|
const batchBusy = ref(false);
|
|
168
173
|
const batchProgress = ref(0);
|
|
169
174
|
const downloadCache = /* @__PURE__ */ new Map();
|
|
175
|
+
let toastTimer;
|
|
176
|
+
function showToast(msg) {
|
|
177
|
+
toast.value = msg;
|
|
178
|
+
clearTimeout(toastTimer);
|
|
179
|
+
toastTimer = setTimeout(() => toast.value = "", 3200);
|
|
180
|
+
}
|
|
170
181
|
function errorText(error) {
|
|
171
182
|
return error instanceof Error ? error.message : String(error);
|
|
172
183
|
}
|
|
@@ -327,14 +338,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
327
338
|
if (payload.code !== 200) throw new ApiError(payload.message || `HTTP ${response.status}`, payload.code ?? response.status);
|
|
328
339
|
return payload.data;
|
|
329
340
|
}
|
|
330
|
-
async function listDir(path) {
|
|
341
|
+
async function listDir(path, password = activePassword.value) {
|
|
331
342
|
if (demo.value) {
|
|
332
343
|
await new Promise((resolve) => setTimeout(resolve, 220));
|
|
333
344
|
return DEMO_TREE[path] ?? [];
|
|
334
345
|
}
|
|
335
346
|
return (await request("/api/fs/list", {
|
|
336
347
|
path,
|
|
337
|
-
password
|
|
348
|
+
password,
|
|
338
349
|
page: 1,
|
|
339
350
|
per_page: 0,
|
|
340
351
|
refresh: false
|
|
@@ -353,7 +364,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
353
364
|
nodes.sort((a, b) => compareNodes(a, b, sortKey.value, sortAsc.value));
|
|
354
365
|
for (const node of nodes) if (node.children.length) sortTree(node.children);
|
|
355
366
|
}
|
|
356
|
-
function toNodes(items, parent, depth) {
|
|
367
|
+
function toNodes(items, parent, depth, password = "") {
|
|
357
368
|
return items.map((item) => ({
|
|
358
369
|
...item,
|
|
359
370
|
path: joinPath(parent, item.name),
|
|
@@ -361,16 +372,82 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
361
372
|
expanded: false,
|
|
362
373
|
loaded: false,
|
|
363
374
|
loading: false,
|
|
364
|
-
children: []
|
|
375
|
+
children: [],
|
|
376
|
+
password
|
|
365
377
|
}));
|
|
366
378
|
}
|
|
379
|
+
function sourceName(path) {
|
|
380
|
+
const parts = path.split("/").filter(Boolean);
|
|
381
|
+
return parts[parts.length - 1] || path;
|
|
382
|
+
}
|
|
383
|
+
async function loadChildren(node) {
|
|
384
|
+
if (node.loaded) return true;
|
|
385
|
+
node.loading = true;
|
|
386
|
+
try {
|
|
387
|
+
node.children = toNodes(await listDir(node.path, node.password), node.path, node.depth + 1, node.password);
|
|
388
|
+
sortTree(node.children);
|
|
389
|
+
node.loaded = true;
|
|
390
|
+
return true;
|
|
391
|
+
} catch (error) {
|
|
392
|
+
showToast(t("folderFailed", { message: errorText(error) }));
|
|
393
|
+
return false;
|
|
394
|
+
} finally {
|
|
395
|
+
node.loading = false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async function loadSourceRoot(source) {
|
|
399
|
+
const path = source.path;
|
|
400
|
+
const password = source.password ?? "";
|
|
401
|
+
let item = {
|
|
402
|
+
name: sourceName(path),
|
|
403
|
+
size: 0,
|
|
404
|
+
is_dir: true,
|
|
405
|
+
modified: ""
|
|
406
|
+
};
|
|
407
|
+
if (demo.value) {
|
|
408
|
+
if (/\/[^/]+\.[^/]+$/.test(path)) item = {
|
|
409
|
+
...item,
|
|
410
|
+
is_dir: false,
|
|
411
|
+
modified: "2026-09-01T08:00:00+08:00"
|
|
412
|
+
};
|
|
413
|
+
} else try {
|
|
414
|
+
const data = await request("/api/fs/get", {
|
|
415
|
+
path,
|
|
416
|
+
password
|
|
417
|
+
});
|
|
418
|
+
item = {
|
|
419
|
+
...item,
|
|
420
|
+
...data
|
|
421
|
+
};
|
|
422
|
+
} catch {}
|
|
423
|
+
return {
|
|
424
|
+
...item,
|
|
425
|
+
name: source.title || item.name || sourceName(path),
|
|
426
|
+
path,
|
|
427
|
+
depth: 0,
|
|
428
|
+
expanded: source.expanded ?? props.expanded,
|
|
429
|
+
loaded: false,
|
|
430
|
+
loading: false,
|
|
431
|
+
children: [],
|
|
432
|
+
password,
|
|
433
|
+
isRoot: true
|
|
434
|
+
};
|
|
435
|
+
}
|
|
367
436
|
async function loadRoot() {
|
|
368
437
|
loadingRoot.value = true;
|
|
369
438
|
errorMsg.value = "";
|
|
370
439
|
needsPassword.value = false;
|
|
371
440
|
try {
|
|
441
|
+
if (props.sources.length) {
|
|
442
|
+
const nodes = await Promise.all(props.sources.map((source) => loadSourceRoot(source)));
|
|
443
|
+
roots.value = nodes;
|
|
444
|
+
for (const node of roots.value) if (node.is_dir && node.expanded) node.expanded = await loadChildren(node);
|
|
445
|
+
selected.value = /* @__PURE__ */ new Set();
|
|
446
|
+
downloadCache.clear();
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
372
449
|
const items = await listDir(props.path);
|
|
373
|
-
roots.value = toNodes(items, props.path, 0);
|
|
450
|
+
roots.value = toNodes(items, props.path, 0, activePassword.value);
|
|
374
451
|
sortTree(roots.value);
|
|
375
452
|
selected.value = /* @__PURE__ */ new Set();
|
|
376
453
|
downloadCache.clear();
|
|
@@ -454,8 +531,9 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
454
531
|
ssrRenderList(visible.value, (node) => {
|
|
455
532
|
_push(`<div class="${ssrRenderClass([{
|
|
456
533
|
"fl-row-dir": node.is_dir,
|
|
534
|
+
"fl-row-root": node.isRoot,
|
|
457
535
|
"fl-row-selected": selected.value.has(node.path)
|
|
458
|
-
}, "fl-row"])}"><span class="fl-cell fl-cell-check"><input type="checkbox"${ssrIncludeBooleanAttr(isChecked(node)) ? " checked" : ""}${ssrRenderAttr("indeterminate", isIndeterminate(node))}></span><span class="fl-cell fl-cell-name" style="${ssrRenderStyle({ paddingLeft: `${node.depth * 22}px` })}">`);
|
|
536
|
+
}, "fl-row"])}"><span class="fl-cell fl-cell-check"><input type="checkbox"${ssrIncludeBooleanAttr(isChecked(node)) ? " checked" : ""}${ssrRenderAttr("indeterminate", isIndeterminate(node))}></span><span class="fl-cell fl-cell-name"${ssrRenderAttr("title", node.isRoot ? node.path : void 0)} style="${ssrRenderStyle({ paddingLeft: `${node.depth * 22}px` })}">`);
|
|
459
537
|
if (node.is_dir) _push(`<button type="button" class="${ssrRenderClass([{ "fl-arrow-open": node.expanded }, "fl-arrow"])}">▸</button>`);
|
|
460
538
|
else _push(`<span class="fl-arrow-placeholder"></span>`);
|
|
461
539
|
_push(`<span class="${ssrRenderClass([{ "fl-name-link": node.is_dir }, "fl-name"])}">`);
|
package/dist/client/style.css
CHANGED
package/package.json
CHANGED