From e4e2d76f75f3fc566a4ce13656e013d14ff25e86 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=98=AD?= <81747598+lan-yonghui@users.noreply.github.com>
Date: Fri, 14 Nov 2025 13:40:52 +0800
Subject: [PATCH] feat: File manager multi-tab start path optimization (#10957)
#10302
---
.../src/views/host/file-management/index.vue | 1283 ++++++++---------
1 file changed, 633 insertions(+), 650 deletions(-)
diff --git a/frontend/src/views/host/file-management/index.vue b/frontend/src/views/host/file-management/index.vue
index f79ab9905..4ccd884a7 100644
--- a/frontend/src/views/host/file-management/index.vue
+++ b/frontend/src/views/host/file-management/index.vue
@@ -14,361 +14,375 @@
:label="item.name == '' ? $t('file.root') : item.name"
:name="item.id"
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
-
-
-
- ..
-
-
-
-
-
- {{
- hidePath.name.length > 25
- ? hidePath.name.substring(0, 22) + '...'
- : hidePath.name
- }}
-
-
-
-
-
- >
-
-
- {{
- path.name.length > 25
- ? path.name.substring(0, 22) + '...'
- : path.name
- }}
-
-
-
-
-
-
- {{
- path.name.length > 25
- ? path.name.substring(0, 22) + '...'
- : path.name
- }}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
- >
-
-
-
- ..
-
-
-
-
-
- {{
- hidePath.name.length > 25
- ? hidePath.name.substring(0, 22) + '...'
- : hidePath.name
- }}
-
-
-
-
-
- >
-
-
- {{
- path.name.length > 25
- ? path.name.substring(0, 22) + '...'
- : path.name
- }}
-
-
-
-
-
-
-
+
-
-
-
-
-
- {{ $t('file.fileHelper') }}
-
-
-
-
-
-
-
-
- {{ $t('commons.button.create') }}
-
-
-
-
-
-
- {{ $t('file.dir') }}
-
-
-
- {{ $t('menu.files') }}
-
-
-
-
-
-
- {{ $t('commons.button.upload') }}/{{ $t('commons.button.download') }}
-
-
-
-
-
-
- {{ $t('commons.button.upload') }}
-
-
-
- {{ $t('file.remoteFile') }}
-
-
-
-
-
-
- {{ $t('file.recycleBin') }}
-
-
- {{ $t('menu.terminal') }}
-
-
-
-
- {{ $t('file.favorite') }}
-
-
-
-
-
-
-
-
-
-
-
- {{ row.name }}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('file.calculate') }}
-
-
-
- {{ hostMount[0]?.path }} ({{ $t('file.root') }})
- {{ formatFileSize(hostMount[0]?.free) }}
-
-
-
-
-
- {{ hostMount[0]?.path }} ({{ $t('file.root') }})
- {{ formatFileSize(hostMount[0]?.free) }}
-
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+ ..
+
-
-
+
- {{ mount.path }} ({{ $t('file.root') }})
- {{ formatFileSize(mount.free) }}
-
- 25
+ ? hidePath.name.substring(0, 22) + '...'
+ : hidePath.name
+ }}
+
+
+
+
+
+ >
+
+
+ {{
+ path.name.length > 25
+ ? path.name.substring(0, 22) + '...'
+ : path.name
+ }}
+
+
+
+
+
+
+ {{
+ path.name.length > 25
+ ? path.name.substring(0, 22) + '...'
+ : path.name
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+ ..
+
+
+
+
+
- {{ mount.path }} ({{ $t('home.mount') }})
- {{ formatFileSize(mount.free) }}
-
-
+ {{
+ hidePath.name.length > 25
+ ? hidePath.name.substring(0, 22) + '...'
+ : hidePath.name
+ }}
+
+
-
-
-
-
- {{ $t('file.wgetTask') }}
-
-
+
+ >
+
+
+ {{
+ path.name.length > 25
+ ? path.name.substring(0, 22) + '...'
+ : path.name
+ }}
+
+
+
+
-
-
-
+
+
+
+
+
+
+ {{ $t('file.fileHelper') }}
+
+
+
+
+
+
+
+
+ {{ $t('commons.button.create') }}
+
+
+
+
+
+
+ {{ $t('file.dir') }}
+
+
+
+ {{ $t('menu.files') }}
+
+
+
+
+
+
+ {{ $t('commons.button.upload') }}/{{ $t('commons.button.download') }}
+
+
+
+
+
+
+ {{ $t('commons.button.upload') }}
+
+
+
+ {{ $t('file.remoteFile') }}
+
+
+
+
+
+
+ {{ $t('file.recycleBin') }}
+
+
+ {{ $t('menu.terminal') }}
+
+
+
+
+ {{ $t('file.favorite') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('file.calculate') }}
+
+
+
+ {{ hostMount[0]?.path }} ({{ $t('file.root') }})
+ {{ formatFileSize(hostMount[0]?.free) }}
+
+
+
+
+
+ {{ hostMount[0]?.path }} ({{ $t('file.root') }})
+ {{ formatFileSize(hostMount[0]?.free) }}
+
+
+
+
+
+ {{ mount.path }} ({{ $t('file.root') }})
+ {{ formatFileSize(mount.free) }}
+
+
+ {{ mount.path }} ({{ $t('home.mount') }})
+ {{ formatFileSize(mount.free) }}
+
+
+
+
+
+
+
+
+
+
+ {{ $t('file.wgetTask') }}
+
+
+
+
+
+
+
+
+
+
+ {{ $t('tabs.more') }}
+
+
+
+
+
+ {{ $t(btn.label) }}
+
+
+
+
+
+
+
+
+
+ {{ $t(btn.label) }}
+
+
+
{{ $t('tabs.more') }}
@@ -387,240 +401,207 @@
-
-
-
-
-
- {{ $t(btn.label) }}
-
-
-
-
-
- {{ $t('tabs.more') }}
-
-
-
-
-
- {{ $t(btn.label) }}
-
-
-
-
-
-
-
-
-
-
- {{ $t('file.paste') }}({{ fileMove.count }})
-
-
-
-
-
-
-
-
-
-
- {{ $t('file.sub') }}
-
-
-
-
-
-
-
+
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
- {{ row.name }}
-
- -> {{ row.linkPath }}
-
-
+
+ {{ $t('file.paste') }}({{ fileMove.count }})
+
+
+
+
+
+
+
+
+
+
+ {{ $t('file.sub') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.name }}
+
+ -> {{ row.linkPath }}
+
+
-
-
-
-
- {{ row.mode }}
-
-
-
-
-
- {{ row.user ? row.user : '-' }} ({{ row.uid }})
-
-
-
-
-
-
- {{ row.group ? row.group : '-' }} ({{ row.gid }})
-
-
-
-
-
-
-
-
- {{ $t('file.calculate') }}
-
- {{ formatFileSize(row.dirSize) }}
-
-
- {{ formatFileSize(row.size) }}
-
-
-
-
-
-
-
-
-
- {{ $t('file.fileDirNum', [dirNum, fileNum]) }}
-
-
- {{ $t('file.currentDir') + $t('file.size') + ' ' }}
-
-
-
- {{ $t('file.calculate') }}
-
-
- {{ formatFileSize(dirTotalSize) }}
-
-
-
-
-
-
+
+
+
+ {{ row.mode }}
+
+
+
+
+
+ {{ row.user ? row.user : '-' }} ({{ row.uid }})
+
+
+
+
+
+
+ {{ row.group ? row.group : '-' }} ({{ row.gid }})
+
+
+
+
+
+
+
+
+ {{ $t('file.calculate') }}
+
+ {{ formatFileSize(row.dirSize) }}
+
+
+ {{ formatFileSize(row.size) }}
+
+
+
+
+
+
+
+
+
+ {{ $t('file.fileDirNum', [dirNum, fileNum]) }}
+
+
+ {{ $t('file.currentDir') + $t('file.size') + ' ' }}
+
+
+
+ {{ $t('file.calculate') }}
+
+
+ {{ formatFileSize(dirTotalSize) }}
+
+
+
+
+
+
+
-
+
@@ -712,6 +693,7 @@ import { CompressExtension, CompressType } from '@/enums/files';
import type { TabPaneName } from 'element-plus';
import { getComponentInfo } from '@/api/modules/host';
import { routerToNameWithQuery } from '@/utils/router';
+import { loadBaseDir } from '@/api/modules/setting';
const globalStore = GlobalStore();
@@ -747,6 +729,7 @@ const initData = () => ({
});
let req = reactive(initData());
let loading = ref(false);
+const baseDir = ref();
const paths = ref([]);
const hidePaths = ref([]);
let pathWidth = ref(0);
@@ -973,14 +956,15 @@ const updateButtons = async () => {
}
};
-const handlePath = () => {
+const handlePath = (depth = 0) => {
+ if (depth > 10) return;
nextTick(function () {
let breadCrumbWidth = breadCrumbRef.value?.offsetWidth;
let pathWidth = toolRef.value?.offsetWidth;
if (pathWidth - breadCrumbWidth < 50 && paths.value.length > 1) {
const removed = paths.value.shift();
if (removed) hidePaths.value.push(removed);
- handlePath();
+ handlePath(depth + 1);
}
});
};
@@ -995,19 +979,22 @@ const btnResizeHandler = debounce(() => {
}, 100);
const observeResize = () => {
- const el = getCurrentPath() as any;
- if (!el) return;
- let resizeObserver = new ResizeObserver(() => {
- resizeHandler();
+ const el = getCurrentPath();
+ const ele = getCurrentBtnWrapper();
+ if (!el || !ele) return;
+
+ const observe = new ResizeObserver((entries) => {
+ const isElChanged = entries.some((entry) => entry.target === el);
+ const isEleChanged = entries.some((entry) => entry.target === ele);
+
+ if (isElChanged) resizeHandler();
+ if (isEleChanged) btnResizeHandler();
});
- const ele = getCurrentBtnWrapper() as any;
- if (!ele) return;
- resizeObserver = new ResizeObserver(() => {
- btnResizeHandler();
- });
- resizeObserver.observe(el);
- resizeObserver.observe(ele);
+ observe.observe(el);
+ observe.observe(ele);
+
+ resizeObserver = observe;
};
function watchTitleHeight() {
@@ -1738,18 +1725,6 @@ function hideRightMenu() {
getCurrentTable().closeRightClick();
}
-onMounted(() => {
- initShowHidden();
- initTabsAndPaths();
- getHostMount();
- initHistory();
- checkFFmpeg();
- nextTick(function () {
- handlePath();
- observeResize();
- });
-});
-
function initShowHidden() {
const showHidden = localStorage.getItem('show-hidden');
if (showHidden === null) {
@@ -1765,7 +1740,6 @@ function initTabsAndPaths() {
let path = getInitialPath();
req.path = path;
getPaths(path);
- editableTabsValue.value = path;
updateTab(path);
paths.value = buildPaths(path);
pathWidth.value = getCurrentPath()?.offsetWidth;
@@ -1788,21 +1762,28 @@ function initHistory() {
function getInitialPath(): string {
const routePath = router.currentRoute.value.query.path;
- if (routePath) {
+ if (routePath != undefined) {
const p = String(routePath);
globalStore.setLastFilePath(p);
return p;
- } else if (globalStore.lastFilePath && globalStore.lastFilePath !== '') {
+ } else if (
+ typeof globalStore.lastFilePath === 'string' &&
+ globalStore.lastFilePath.trim() !== '' &&
+ globalStore.lastFilePath !== 'undefined'
+ ) {
return globalStore.lastFilePath;
}
+ const tab = editableTabs.value.find((t) => t.id === editableTabsKey.value);
+ if (tab) {
+ globalStore.setLastFilePath(tab.path);
+ return tab.path;
+ }
return '/';
}
const editableTabsKey = ref('');
-const editableTabsValue = ref('');
-const editableTabsName = ref('');
const editableTabs = ref([
- { id: '1', name: 'opt', path: '/opt' },
+ { id: '1', name: getLastPath(baseDir.value), path: baseDir.value },
{ id: '2', name: 'home', path: '/home' },
]);
@@ -1811,17 +1792,9 @@ function initTabs() {
if (savedTabs) {
editableTabs.value = JSON.parse(savedTabs);
}
-
const savedTabsKey = localStorage.getItem('editableTabsKey');
if (savedTabsKey) {
editableTabsKey.value = savedTabsKey;
- const tab = editableTabs.value.find((t) => t.id === savedTabsKey);
- if (tab) {
- editableTabsValue.value = tab.path;
- editableTabsName.value = tab.name;
- } else {
- setFirstTab();
- }
} else {
setFirstTab();
}
@@ -1831,19 +1804,18 @@ function setFirstTab() {
if (editableTabs.value.length > 0) {
const first = editableTabs.value[0];
editableTabsKey.value = first.id;
- editableTabsValue.value = first.path;
- editableTabsName.value = first.name;
+ } else {
+ initTabs();
}
}
-watch(
- [editableTabs, editableTabsKey],
- ([newTabs, newKey]) => {
- localStorage.setItem('editableTabs', JSON.stringify(newTabs));
- localStorage.setItem('editableTabsKey', newKey);
- },
- { deep: true },
-);
+function saveStorageTabs() {
+ localStorage.setItem('editableTabs', JSON.stringify(editableTabs.value));
+}
+
+function saveStorageTabsKey() {
+ localStorage.setItem('editableTabsKey', editableTabsKey.value);
+}
function getLastPath(path: string): string {
if (!path) return '';
@@ -1852,58 +1824,56 @@ function getLastPath(path: string): string {
}
function updateTab(newPath?: string) {
- const tab = editableTabs.value.find((t) => t.id === editableTabsKey.value);
- if (tab) {
- tab.path = newPath;
- tab.name = getLastPath(newPath);
- }
+ editableTabs.value = editableTabs.value.map((tab) => {
+ if (tab.id === editableTabsKey.value) {
+ return {
+ ...tab,
+ path: newPath,
+ name: getLastPath(newPath),
+ };
+ }
+ return tab;
+ });
+ saveStorageTabs();
}
+const loadPath = async () => {
+ const pathRes = await loadBaseDir();
+ baseDir.value = pathRes.data;
+};
+
const addTab = () => {
if (editableTabs.value.length >= 6) {
MsgWarning(i18n.global.t('file.notCanTab'));
return;
}
- const usedIds = editableTabs.value.map((t) => Number(t.id));
- let newId = null;
- for (let i = 1; i <= 6; i++) {
- if (!usedIds.includes(i)) {
- newId = i;
- break;
- }
- }
- if (newId === null) {
+ const usedIds = new Set(editableTabs.value.map((t) => Number(t.id)));
+ const newId = Array.from({ length: 6 }, (_, i) => i + 1).find((id) => !usedIds.has(id));
+
+ if (!newId) {
MsgWarning(i18n.global.t('file.notCanTab'));
return;
}
editableTabs.value.push({
id: String(newId),
- name: 'opt',
- path: '/opt',
+ name: getLastPath(baseDir.value),
+ path: baseDir.value,
});
editableTabsKey.value = String(newId);
- changeTab(String(newId));
+ changeTab(newId);
};
const changeTab = (targetPath: TabPaneName) => {
if (targetPath === 99) {
return;
}
- editableTabsKey.value = targetPath.toString();
- const current = editableTabs.value.find((tab) => tab.id === editableTabsKey.value);
- editableTabsName.value = current ? current.name : '';
- editableTabsValue.value = current ? current.path : '';
- req.path = editableTabsValue.value;
- paths.value = [];
- const segments = editableTabsValue.value.split('/').filter(Boolean);
- let url = '';
- segments.forEach((segment) => {
- url += '/' + segment;
- paths.value.push({
- url,
- name: segment,
- });
- });
+ const current = editableTabs.value.find((tab) => tab.id === targetPath.toString());
+ editableTabsKey.value = current.id;
+ saveStorageTabs();
+ saveStorageTabsKey();
+ req.path = current ? current.path : '';
+ globalStore.setLastFilePath(req.path);
+ getPaths(req.path);
search();
};
@@ -1923,7 +1893,7 @@ const removeTab = (targetId: TabPaneName) => {
}
editableTabs.value = tabs.filter((t) => String(t.id) !== target);
editableTabsKey.value = String(nextActive);
- changeTab(String(nextActive));
+ changeTab(nextActive);
};
const checkFFmpeg = () => {
@@ -1932,6 +1902,19 @@ const checkFFmpeg = () => {
});
};
+onMounted(() => {
+ loadPath();
+ initShowHidden();
+ initTabsAndPaths();
+ getHostMount();
+ initHistory();
+ checkFFmpeg();
+ nextTick(function () {
+ handlePath();
+ observeResize();
+ });
+});
+
onBeforeUnmount(() => {
if (resizeObserver) resizeObserver.disconnect();
window.removeEventListener('resize', watchTitleHeight);