diff --git a/web/src/views/file/CompressModal.vue b/web/src/views/file/CompressModal.vue index dda47d131..f3e4b5e16 100644 --- a/web/src/views/file/CompressModal.vue +++ b/web/src/views/file/CompressModal.vue @@ -5,9 +5,11 @@ import { useGettext } from 'vue3-gettext' import api from '@/api/panel/file' import { useFileStore } from '@/stores' import { generateRandomString, lastDirectory } from '@/utils' +import { useFileOps } from '@/views/file/composables/useFileOps' const { $gettext } = useGettext() const fileStore = useFileStore() +const { refreshAfterTasks } = useFileOps() const show = defineModel('show', { type: Boolean, required: true }) const path = defineModel('path', { type: String, required: true }) // 打开时快照选中项,弹窗内移除不影响列表选中状态 @@ -39,6 +41,7 @@ const handleArchive = () => { window.$message.success( $gettext('Compress task created successfully, please check the task list for progress'), ) + refreshAfterTasks() }) .onComplete(() => { loading.value = false diff --git a/web/src/views/file/EditModal.vue b/web/src/views/file/EditModal.vue index 1a6df57a9..3c2507940 100644 --- a/web/src/views/file/EditModal.vue +++ b/web/src/views/file/EditModal.vue @@ -99,6 +99,12 @@ function loadFile(path: string) { openInEditor(path) } +// 列表重新可见时恢复快捷键,并刷新以同步保存后的大小和修改时间 +function releaseList() { + window.$bus.emit('file:keyboard-resume') + window.$bus.emit('file:refresh') +} + // 打开时自动加载文件 watch(show, (newShow) => { if (newShow && filePath.value) { @@ -112,8 +118,7 @@ watch(show, (newShow) => { // 加载文件 loadFile(filePath.value) } else if (!newShow) { - // 恢复文件管理的键盘快捷键 - window.$bus.emit('file:keyboard-resume') + releaseList() } }) @@ -127,7 +132,7 @@ watch(filePath, (newPath) => { // 监听最小化状态 watch(minimized, (isMinimized) => { if (isMinimized) { - window.$bus.emit('file:keyboard-resume') + releaseList() } else { window.$bus.emit('file:keyboard-pause') } diff --git a/web/src/views/file/ListView.vue b/web/src/views/file/ListView.vue index 917e187bb..b82800963 100644 --- a/web/src/views/file/ListView.vue +++ b/web/src/views/file/ListView.vue @@ -39,7 +39,7 @@ const { $gettext } = useGettext() const themeVars = useThemeVars() const fileStore = useFileStore() const { handlePaste: doPaste } = usePaste() -const { deletePaths, movePath, markClipboard } = useFileOps() +const { deletePaths, movePath, markClipboard, refreshAfterTasks } = useFileOps() const props = defineProps<{ tabId: string @@ -774,6 +774,10 @@ const openTerminal = (item: any) => { terminalModal.value = true } +watch(terminalModal, (show) => { + if (!show) window.$bus.emit('file:refresh') +}) + // 打开属性弹窗 const openProperty = (item: any) => { propertyFileInfo.value = item as FileInfo @@ -1291,6 +1295,7 @@ const handleUnCompress = () => { window.$message.success( $gettext('Uncompress task created successfully, please check the task list for progress'), ) + refreshAfterTasks() }, ) } diff --git a/web/src/views/file/ToolBar.vue b/web/src/views/file/ToolBar.vue index 50d02ede2..106f0162f 100644 --- a/web/src/views/file/ToolBar.vue +++ b/web/src/views/file/ToolBar.vue @@ -12,7 +12,7 @@ const { $gettext } = useGettext() const fileStore = useFileStore() const uploadStore = useUploadStore() const { handlePaste: doPaste } = usePaste() -const { deletePaths, markClipboard } = useFileOps() +const { deletePaths, markClipboard, refreshAfterTasks } = useFileOps() const props = defineProps<{ tabId: string @@ -29,6 +29,9 @@ const selected = computed(() => tab.value.selected) // 终端弹窗 const terminalModal = ref(false) +watch(terminalModal, (show) => { + if (!show) window.$bus.emit('file:refresh') +}) const download = ref(false) const downloadLoading = ref(false) @@ -57,6 +60,7 @@ const handleDownload = () => { window.$message.success( $gettext('Download task created successfully, please check the task list for progress'), ) + refreshAfterTasks() }) .onComplete(() => { downloadLoading.value = false diff --git a/web/src/views/file/composables/useFileOps.ts b/web/src/views/file/composables/useFileOps.ts index e9b40fb37..22229b258 100644 --- a/web/src/views/file/composables/useFileOps.ts +++ b/web/src/views/file/composables/useFileOps.ts @@ -1,9 +1,14 @@ +import { promiseTimeout } from '@vueuse/core' import { useGettext } from 'vue3-gettext' import file from '@/api/panel/file' +import task from '@/api/panel/task' import { useEditorStore, useFileStore } from '@/stores' import { lastDirectory } from '@/utils/file' +// 多处同时提交任务时共用一个轮询 +let waitingTasks = false + // 文件操作的统一入口 // 操作成功后的编辑器标签页同步、列表刷新、选中清理在此收敛,调用方只负责确认交互 // 请求失败的错误弹窗由全局拦截器负责 @@ -56,5 +61,21 @@ export function useFileOps() { ) } - return { deletePaths, movePath, markClipboard } + // 压缩、解压、远程下载是后台任务,结果要等任务跑完才出现,轮询到队列清空再刷新列表 + async function refreshAfterTasks() { + if (waitingTasks) return + waitingTasks = true + try { + do { + await promiseTimeout(1000) + } while ((await task.status()).task) + window.$bus.emit('file:refresh') + } catch { + /* empty */ + } finally { + waitingTasks = false + } + } + + return { deletePaths, movePath, markClipboard, refreshAfterTasks } }