feat: File manager multi-tab start path optimization (#10957)

#10302
This commit is contained in:
2025-11-14 13:40:52 +08:00 committed by GitHub
parent 282e196534
commit e4e2d76f75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -14,7 +14,6 @@
:label="item.name == '' ? $t('file.root') : item.name"
:name="item.id"
>
<div>
<div class="flex sm:flex-row flex-col justify-start gap-y-2 items-center gap-x-4" ref="toolRef">
<div class="flex-shrink-0 flex sm:w-min w-full items-center justify-start">
<el-tooltip :content="$t('file.back')" placement="top">
@ -54,11 +53,7 @@
<el-icon :size="20"><HomeFilled /></el-icon>
</el-link>
</span>
<span
v-for="(path, index) in paths"
:key="path.url"
class="inline-flex items-center"
>
<span v-for="(path, index) in paths" :key="path.url" class="inline-flex items-center">
<span class="mr-2 arrow">></span>
<template v-if="index === 0 && hidePaths.length > 0">
<el-dropdown>
@ -151,11 +146,7 @@
<el-icon :size="20"><HomeFilled /></el-icon>
</el-link>
</span>
<span
v-for="(path, index) in paths"
:key="path.url"
class="inline-flex items-center"
>
<span v-for="(path, index) in paths" :key="path.url" class="inline-flex items-center">
<span class="mr-2 arrow">></span>
<template v-if="index === 0 && hidePaths.length > 0">
<el-dropdown>
@ -187,6 +178,8 @@
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<template v-else>
<span class="mr-2 arrow">></span>
<el-tooltip
class="box-item"
@ -337,17 +330,11 @@
<template #dropdown>
<el-dropdown-menu>
<template v-for="(mount, index) in hostMount" :key="mount.path">
<el-dropdown-item
v-if="index == 0"
@click.stop="jump(mount.path)"
>
<el-dropdown-item v-if="index == 0" @click.stop="jump(mount.path)">
{{ mount.path }} ({{ $t('file.root') }})
{{ formatFileSize(mount.free) }}
</el-dropdown-item>
<el-dropdown-item
v-if="index != 0"
@click.stop="jump(mount.path)"
>
<el-dropdown-item v-if="index != 0" @click.stop="jump(mount.path)">
{{ mount.path }} ({{ $t('home.mount') }})
{{ formatFileSize(mount.free) }}
</el-dropdown-item>
@ -554,12 +541,7 @@
</el-link>
</template>
</el-table-column>
<el-table-column
:label="$t('file.size')"
prop="size"
min-width="100"
:sortable="'custom'"
>
<el-table-column :label="$t('file.size')" prop="size" min-width="100" :sortable="'custom'">
<template #default="{ row }">
<el-button
type="primary"
@ -618,9 +600,8 @@
</ComplexTable>
</template>
</LayoutContent>
</div>
</el-tab-pane>
<el-tab-pane :closable="false" :disabled="editableTabs.length > 6">
<el-tab-pane :name="editableTabsKey" :closable="false" :disabled="editableTabs.length > 6">
<template #label>
<el-icon @click="addTab()"><Plus /></el-icon>
</template>
@ -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<FilePaths[]>([]);
const hidePaths = ref<FilePaths[]>([]);
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);