From c2c3aa48f1468ed8e0d4020d60369ef06526cc9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Thu, 29 Jan 2026 23:37:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B8=85=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/package.json | 2 +- web/src/components/common/CronPreview.vue | 2 +- web/src/components/common/DraggableWindow.vue | 1 - web/src/components/common/KeyValueEditor.vue | 2 +- .../file-editor/EditorStatusBar.vue | 2 +- web/src/components/file-editor/FileTree.vue | 11 ++--- .../layout/sidebar/components/SideMenu.vue | 3 +- web/src/layout/tab/components/ContextMenu.vue | 4 +- web/src/router/routes/index.ts | 3 +- web/src/store/modules/editor/index.ts | 8 ++-- web/src/store/modules/tab/index.ts | 10 ++--- web/src/store/modules/theme/index.ts | 4 +- web/src/utils/common/base64.ts | 2 +- web/src/utils/common/common.ts | 7 ++-- web/src/views/backup/UploadModal.vue | 1 - web/src/views/container/ContainerCreate.vue | 2 +- web/src/views/file/EditModal.vue | 2 - web/src/views/file/IndexView.vue | 2 +- web/src/views/file/ListView.vue | 5 ++- web/src/views/file/PathInput.vue | 4 +- web/src/views/file/PermissionModal.vue | 14 ++++--- web/src/views/file/ToolBar.vue | 3 +- web/src/views/home/IndexView.vue | 4 +- web/src/views/setting/SettingBase.vue | 4 +- web/src/views/ssh/IndexView.vue | 5 ++- web/src/views/toolbox/BenchmarkView.vue | 2 +- web/src/views/toolbox/DiskView.vue | 1 + web/src/views/toolbox/LogView.vue | 42 ++++++++++++------- web/src/views/website/BulkCreateModal.vue | 9 ++-- 29 files changed, 89 insertions(+), 72 deletions(-) diff --git a/web/package.json b/web/package.json index 36b1387c..7fbc1a07 100644 --- a/web/package.json +++ b/web/package.json @@ -15,7 +15,7 @@ "build": "run-s gen-auto-import type-check build-only copy", "preview": "vite preview", "build-only": "vite build", - "type-check": "vue-tsc --noEmit", + "type-check": "vue-tsc --noEmit -p tsconfig.app.json", "lint": "run-s gen-auto-import lint-only", "lint-only": "eslint . --fix", "format": "prettier --write src/", diff --git a/web/src/components/common/CronPreview.vue b/web/src/components/common/CronPreview.vue index 47c4ff3b..375ae010 100644 --- a/web/src/components/common/CronPreview.vue +++ b/web/src/components/common/CronPreview.vue @@ -39,7 +39,7 @@ const parseDescription = computed((): string => { return $gettext('Cron expression: %{cron}', { cron }) } - const [minute, hour, day, month, weekday] = parts + const [minute, hour, day, month, weekday] = parts as [string, string, string, string, string] try { // 每 N 分钟:*/N * * * * diff --git a/web/src/components/common/DraggableWindow.vue b/web/src/components/common/DraggableWindow.vue index 76f8595a..0dc481ce 100644 --- a/web/src/components/common/DraggableWindow.vue +++ b/web/src/components/common/DraggableWindow.vue @@ -27,7 +27,6 @@ const minimized = defineModel('minimized', { default: false }) // 窗口状态 const isMaximized = ref(false) -const windowRef = ref() // 窗口位置和大小 const position = ref({ x: 0, y: 0 }) diff --git a/web/src/components/common/KeyValueEditor.vue b/web/src/components/common/KeyValueEditor.vue index c5914d6b..39d196d0 100644 --- a/web/src/components/common/KeyValueEditor.vue +++ b/web/src/components/common/KeyValueEditor.vue @@ -69,7 +69,7 @@ const updateKey = (oldKey: string, newKey: string) => { if (props.modelValue[newKey] !== undefined) return // 键已存在 const data = { ...props.modelValue } - data[newKey] = data[oldKey] + data[newKey] = data[oldKey] ?? '' delete data[oldKey] emit('update:modelValue', data) } diff --git a/web/src/components/file-editor/EditorStatusBar.vue b/web/src/components/file-editor/EditorStatusBar.vue index 958761e3..c7eb95ca 100644 --- a/web/src/components/file-editor/EditorStatusBar.vue +++ b/web/src/components/file-editor/EditorStatusBar.vue @@ -65,7 +65,7 @@ function handleLanguageChange(value: string) { // 更新缩进 function handleIndentChange(value: string) { - const [size, type] = value.split('-') + const [size, type] = value.split('-') as [string, string] editorStore.updateSettings({ tabSize: parseInt(size), insertSpaces: type === 'spaces' diff --git a/web/src/components/file-editor/FileTree.vue b/web/src/components/file-editor/FileTree.vue index 87e06d5e..f219975c 100644 --- a/web/src/components/file-editor/FileTree.vue +++ b/web/src/components/file-editor/FileTree.vue @@ -187,17 +187,18 @@ function showCreate(type: 'file' | 'dir') { // 确定父目录 if (selectedKeys.value.length > 0) { - const selectedNode = findNode(treeData.value, selectedKeys.value[0]) + const selectedKey = selectedKeys.value[0] ?? '' + const selectedNode = findNode(treeData.value, selectedKey) if (selectedNode && !selectedNode.isLeaf) { // 选中的是目录,在该目录下新建 - inlineCreateParentPath.value = selectedKeys.value[0] + inlineCreateParentPath.value = selectedKey // 确保目录已展开 - if (!expandedKeys.value.includes(selectedKeys.value[0])) { - expandedKeys.value = [...expandedKeys.value, selectedKeys.value[0]] + if (!expandedKeys.value.includes(selectedKey)) { + expandedKeys.value = [...expandedKeys.value, selectedKey] } } else { // 选中的是文件,在其父目录下新建 - const parts = selectedKeys.value[0].split('/') + const parts = selectedKey.split('/') parts.pop() inlineCreateParentPath.value = parts.join('/') || props.rootPath } diff --git a/web/src/layout/sidebar/components/SideMenu.vue b/web/src/layout/sidebar/components/SideMenu.vue index 2dccf225..d4f17d59 100644 --- a/web/src/layout/sidebar/components/SideMenu.vue +++ b/web/src/layout/sidebar/components/SideMenu.vue @@ -3,7 +3,8 @@ import { translateTitle } from '@/locales/menu' import { usePermissionStore, useTabStore, useThemeStore } from '@/store' import { isUrl, renderIcon } from '@/utils' -import { MenuInst, MenuOption, useThemeVars } from 'naive-ui' +import type { MenuInst, MenuOption } from 'naive-ui' +import { useThemeVars } from 'naive-ui' import type { VNodeChild } from 'vue' import { RouterLink } from 'vue-router' import type { Meta, RouteType } from '~/types/router' diff --git a/web/src/layout/tab/components/ContextMenu.vue b/web/src/layout/tab/components/ContextMenu.vue index de5d1823..4ebee6ff 100644 --- a/web/src/layout/tab/components/ContextMenu.vue +++ b/web/src/layout/tab/components/ContextMenu.vue @@ -57,7 +57,7 @@ const options = computed(() => [ { label: $gettext('Close Left'), key: 'close-left', - disabled: tabStore.tabs.length <= 1 || props.currentPath === tabStore.tabs[0].path, + disabled: tabStore.tabs.length <= 1 || props.currentPath === tabStore.tabs[0]?.path, icon: renderIcon('mdi:arrow-expand-left', { size: 14 }) }, { @@ -65,7 +65,7 @@ const options = computed(() => [ key: 'close-right', disabled: tabStore.tabs.length <= 1 || - props.currentPath === tabStore.tabs[tabStore.tabs.length - 1].path, + props.currentPath === tabStore.tabs[tabStore.tabs.length - 1]?.path, icon: renderIcon('mdi:arrow-expand-right', { size: 14 }) } ]) diff --git a/web/src/router/routes/index.ts b/web/src/router/routes/index.ts index 14c081af..7c3cbd37 100644 --- a/web/src/router/routes/index.ts +++ b/web/src/router/routes/index.ts @@ -38,7 +38,8 @@ const modules = import.meta.glob('@/views/**/route.ts', { }) as RouteModule const asyncRoutes: RoutesType = [] Object.keys(modules).forEach((key) => { - asyncRoutes.push(modules[key].default) + const route = modules[key]?.default + if (route) asyncRoutes.push(route) }) export { asyncRoutes } diff --git a/web/src/store/modules/editor/index.ts b/web/src/store/modules/editor/index.ts index cebcb353..7b1a0e21 100644 --- a/web/src/store/modules/editor/index.ts +++ b/web/src/store/modules/editor/index.ts @@ -137,9 +137,9 @@ export const useEditorStore = defineStore('editor', { if (this.tabs.length === 0) { this.activeTabPath = null } else if (index >= this.tabs.length) { - this.activeTabPath = this.tabs[this.tabs.length - 1].path + this.activeTabPath = this.tabs[this.tabs.length - 1]?.path ?? null } else { - this.activeTabPath = this.tabs[index].path + this.activeTabPath = this.tabs[index]?.path ?? null } } }, @@ -162,7 +162,7 @@ export const useEditorStore = defineStore('editor', { if (this.tabs.length === 0) { this.activeTabPath = null } else if (!this.tabs.find((tab) => tab.path === this.activeTabPath)) { - this.activeTabPath = this.tabs[0].path + this.activeTabPath = this.tabs[0]?.path ?? null } }, @@ -255,7 +255,7 @@ export const useEditorStore = defineStore('editor', { if (toIndex < 0 || toIndex >= this.tabs.length) return const [movedTab] = this.tabs.splice(fromIndex, 1) - this.tabs.splice(toIndex, 0, movedTab) + if (movedTab) this.tabs.splice(toIndex, 0, movedTab) } }, diff --git a/web/src/store/modules/tab/index.ts b/web/src/store/modules/tab/index.ts index 57b82670..714fb289 100644 --- a/web/src/store/modules/tab/index.ts +++ b/web/src/store/modules/tab/index.ts @@ -64,28 +64,28 @@ export const useTabStore = defineStore('tab', { removeTab(path: string) { if (path === this.active) { const activeIndex = this.tabs.findIndex((item) => item.path === path) - if (activeIndex > 0) router.push(this.tabs[activeIndex - 1].path) - else router.push(this.tabs[activeIndex + 1].path) + if (activeIndex > 0) router.push(this.tabs[activeIndex - 1]?.path ?? '/') + else router.push(this.tabs[activeIndex + 1]?.path ?? '/') } this.setTabs(this.tabs.filter((tab) => tab.path !== path)) }, removeOther(curPath: string) { this.setTabs(this.tabs.filter((tab) => tab.path === curPath)) - if (curPath !== this.active) router.push(this.tabs[this.tabs.length - 1].path) + if (curPath !== this.active) router.push(this.tabs[this.tabs.length - 1]?.path ?? '/') }, removeLeft(curPath: string) { const curIndex = this.tabs.findIndex((item) => item.path === curPath) const filterTabs = this.tabs.filter((item, index) => index >= curIndex) this.setTabs(filterTabs) if (!filterTabs.find((item) => item.path === this.active)) - router.push(filterTabs[filterTabs.length - 1].path) + router.push(filterTabs[filterTabs.length - 1]?.path ?? '/') }, removeRight(curPath: string) { const curIndex = this.tabs.findIndex((item) => item.path === curPath) const filterTabs = this.tabs.filter((item, index) => index <= curIndex) this.setTabs(filterTabs) if (!filterTabs.find((item) => item.path === this.active)) - router.push(filterTabs[filterTabs.length - 1].path) + router.push(filterTabs[filterTabs.length - 1]?.path ?? '/') }, resetTabs() { this.setTabs([]) diff --git a/web/src/store/modules/theme/index.ts b/web/src/store/modules/theme/index.ts index ac69d419..8d4c1dae 100644 --- a/web/src/store/modules/theme/index.ts +++ b/web/src/store/modules/theme/index.ts @@ -20,10 +20,10 @@ export const useThemeStore = defineStore('theme', { return this.darkMode ? darkTheme : undefined }, naiveLocale(): NLocale { - return locales[this.locale].locale + return locales[this.locale]?.locale ?? enUS }, naiveDateLocale(): NDateLocale { - return locales[this.locale].dateLocale + return locales[this.locale]?.dateLocale ?? dateEnUS } }, actions: { diff --git a/web/src/utils/common/base64.ts b/web/src/utils/common/base64.ts index fad398b5..df954889 100644 --- a/web/src/utils/common/base64.ts +++ b/web/src/utils/common/base64.ts @@ -11,7 +11,7 @@ export function encodeBase64(str: string): string { const bytes = new TextEncoder().encode(str) let binary = '' for (let i = 0; i < bytes.length; i++) { - binary += String.fromCharCode(bytes[i]) + binary += String.fromCharCode(bytes[i] ?? 0) } return btoa(binary) } diff --git a/web/src/utils/common/common.ts b/web/src/utils/common/common.ts index 79540a9e..86883255 100644 --- a/web/src/utils/common/common.ts +++ b/web/src/utils/common/common.ts @@ -47,9 +47,10 @@ export function generateRandomString(length: number) { for (let i = 0; i < randomBytes.length && result.length < length; i++) { // Only use values that map evenly to the character set to avoid bias const maxValue = Math.floor(256 / charactersLength) * charactersLength - if (randomBytes[i] < maxValue) { - const randomIndex = randomBytes[i] % charactersLength - result += characters[randomIndex] + const byteValue = randomBytes[i] ?? 0 + if (byteValue < maxValue) { + const randomIndex = byteValue % charactersLength + result += characters[randomIndex] ?? '' } } } diff --git a/web/src/views/backup/UploadModal.vue b/web/src/views/backup/UploadModal.vue index ee39fa52..6146f99f 100644 --- a/web/src/views/backup/UploadModal.vue +++ b/web/src/views/backup/UploadModal.vue @@ -7,7 +7,6 @@ import api from '@/api/panel/backup' const { $gettext } = useGettext() const show = defineModel('show', { type: Boolean, required: true }) const type = defineModel('type', { type: String, required: true }) -const upload = ref(null) const uploadRequest = ({ file, onFinish, onError, onProgress }: UploadCustomRequestOptions) => { const formData = new FormData() diff --git a/web/src/views/container/ContainerCreate.vue b/web/src/views/container/ContainerCreate.vue index a23dde08..211a75e6 100644 --- a/web/src/views/container/ContainerCreate.vue +++ b/web/src/views/container/ContainerCreate.vue @@ -94,7 +94,7 @@ const getNetworks = () => { value: item.id })) if (networks.value.length > 0) { - createModel.network = networks.value[0].value + createModel.network = networks.value[0]?.value ?? '' } }) } diff --git a/web/src/views/file/EditModal.vue b/web/src/views/file/EditModal.vue index 74d14e8e..ae201537 100644 --- a/web/src/views/file/EditModal.vue +++ b/web/src/views/file/EditModal.vue @@ -13,8 +13,6 @@ const show = defineModel('show', { type: Boolean, required: true }) const minimized = defineModel('minimized', { type: Boolean, default: false }) const filePath = defineModel('file', { type: String, required: true }) -const editorRef = ref>() - // 窗口默认尺寸 const defaultWidth = Math.min(1400, window.innerWidth * 0.9) const defaultHeight = Math.min(900, window.innerHeight * 0.85) diff --git a/web/src/views/file/IndexView.vue b/web/src/views/file/IndexView.vue index 641612ed..1a9119d8 100644 --- a/web/src/views/file/IndexView.vue +++ b/web/src/views/file/IndexView.vue @@ -120,7 +120,7 @@ const handleDrop = async (e: DragEvent) => { // 使用 webkitGetAsEntry 来支持文件夹 for (let i = 0; i < items.length; i++) { const item = items[i] - if (item.kind === 'file') { + if (item?.kind === 'file') { const entry = item.webkitGetAsEntry() if (entry) { if (entry.isFile) { diff --git a/web/src/views/file/ListView.vue b/web/src/views/file/ListView.vue index a2c03013..7d9ca8e4 100644 --- a/web/src/views/file/ListView.vue +++ b/web/src/views/file/ListView.vue @@ -184,7 +184,7 @@ const selectionBox = computed(() => { const hexToRgb = (hex: string) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) return result - ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}` + ? `${parseInt(result[1] ?? '0', 16)}, ${parseInt(result[2] ?? '0', 16)}, ${parseInt(result[3] ?? '0', 16)}` : '24, 160, 88' } @@ -843,7 +843,8 @@ const handlePaste = () => { for (let i = 0; i < data.length; i++) { if (data[i]) { flag = true - paths[i].force = true + const pathItem = paths[i] + if (pathItem) pathItem.force = true } } if (flag) { diff --git a/web/src/views/file/PathInput.vue b/web/src/views/file/PathInput.vue index b51094a8..dc8ae29f 100644 --- a/web/src/views/file/PathInput.vue +++ b/web/src/views/file/PathInput.vue @@ -53,7 +53,7 @@ const handleUp = () => { const handleBack = () => { if (current > 0) { current-- - path.value = history[current] + path.value = history[current] ?? '/' input.value = path.value.slice(1) } } @@ -61,7 +61,7 @@ const handleBack = () => { const handleForward = () => { if (current < history.length - 1) { current++ - path.value = history[current] + path.value = history[current] ?? '/' input.value = path.value.slice(1) } } diff --git a/web/src/views/file/PermissionModal.vue b/web/src/views/file/PermissionModal.vue index 1653d7b9..a293d485 100644 --- a/web/src/views/file/PermissionModal.vue +++ b/web/src/views/file/PermissionModal.vue @@ -34,10 +34,12 @@ watch( (newVal) => { if (newVal && fileInfoList.value.length > 0) { const firstFile = fileInfoList.value[0] - mode.value = normalizeMode(firstFile.mode) - owner.value = firstFile.owner || 'www' - group.value = firstFile.group || 'www' - updateCheckboxes() + if (firstFile) { + mode.value = normalizeMode(firstFile.mode) + owner.value = firstFile.owner || 'www' + group.value = firstFile.group || 'www' + updateCheckboxes() + } } } ) @@ -73,7 +75,7 @@ const calculateMode = () => { const updateCheckboxes = () => { const paddedMode = normalizeMode(mode.value) - const permissions = paddedMode.split('').map(Number) + const permissions = paddedMode.split('').map(Number) as [number, number, number] checkbox.value.owner = permissions[0] & 4 ? ['read'] : [] if (permissions[0] & 2) checkbox.value.owner.push('write') @@ -94,7 +96,7 @@ const title = computed(() => { } return selected.value.length > 1 ? $gettext('Batch modify permissions') - : $gettext('Modify permissions - %{ path }', { path: selected.value[0] }) + : $gettext('Modify permissions - %{ path }', { path: selected.value[0] ?? '' }) }) watch(mode, updateCheckboxes, { immediate: true }) diff --git a/web/src/views/file/ToolBar.vue b/web/src/views/file/ToolBar.vue index 37ba905d..79a7f468 100644 --- a/web/src/views/file/ToolBar.vue +++ b/web/src/views/file/ToolBar.vue @@ -105,7 +105,8 @@ const handlePaste = () => { for (let i = 0; i < data.length; i++) { if (data[i]) { flag = true - paths[i].force = true + const pathItem = paths[i] + if (pathItem) pathItem.force = true } } if (flag) { diff --git a/web/src/views/home/IndexView.vue b/web/src/views/home/IndexView.vue index 363d04d1..9e017822 100644 --- a/web/src/views/home/IndexView.vue +++ b/web/src/views/home/IndexView.vue @@ -540,13 +540,13 @@ if (import.meta.hot) { {{ $gettext('Model') }} - {{ realtime.cpus[0].modelName }} + {{ realtime.cpus[0]?.modelName }} {{ $gettext('Parameters') }} {{ realtime.cpus.length }} CPU {{ cores }} {{ $gettext('cores') }} - {{ formatBytes(realtime.cpus[0].cacheSize * 1024) }} {{ $gettext('cache') }} + {{ formatBytes((realtime.cpus[0]?.cacheSize ?? 0) * 1024) }} {{ $gettext('cache') }} diff --git a/web/src/views/setting/SettingBase.vue b/web/src/views/setting/SettingBase.vue index 23078167..ff3efa7e 100644 --- a/web/src/views/setting/SettingBase.vue +++ b/web/src/views/setting/SettingBase.vue @@ -83,6 +83,7 @@ const getOption = (route: RouteType): TreeSelectOption => { if (visibleChildren.length === 1) { // 单个子路由处理 const singleRoute = visibleChildren[0] + if (!singleRoute) return menuItem const isSingleDisabled = forbiddenHiddenMenus.includes(singleRoute.name as string) menuItem.label = singleRoute.meta?.title ? translateTitle(singleRoute.meta.title) @@ -93,7 +94,8 @@ const getOption = (route: RouteType): TreeSelectOption => { ? singleRoute.children.filter((item: RouteType) => item.name && !item.isHidden) : [] - if (visibleItems.length === 1) menuItem = getOption(visibleItems[0]) + const firstVisibleItem = visibleItems[0] + if (visibleItems.length === 1 && firstVisibleItem) menuItem = getOption(firstVisibleItem) else if (visibleItems.length > 1) menuItem.children = visibleItems.map((item) => getOption(item)) } else { diff --git a/web/src/views/ssh/IndexView.vue b/web/src/views/ssh/IndexView.vue index ad0ff547..078f2fd8 100644 --- a/web/src/views/ssh/IndexView.vue +++ b/web/src/views/ssh/IndexView.vue @@ -180,13 +180,14 @@ const closeTab = (tabId: string) => { if (index === -1) return const tab = tabs.value[index] - disposeTab(tab) + if (tab) disposeTab(tab) tabs.value.splice(index, 1) // 如果关闭的是当前标签,切换到其他标签 if (activeTabId.value === tabId && tabs.value.length > 0) { const newIndex = Math.min(index, tabs.value.length - 1) - switchTab(tabs.value[newIndex].id) + const newTab = tabs.value[newIndex] + if (newTab) switchTab(newTab.id) } // 如果没有标签了,创建一个本机标签 diff --git a/web/src/views/toolbox/BenchmarkView.vue b/web/src/views/toolbox/BenchmarkView.vue index 07006c3a..b75c9156 100644 --- a/web/src/views/toolbox/BenchmarkView.vue +++ b/web/src/views/toolbox/BenchmarkView.vue @@ -64,7 +64,7 @@ const handleTest = async () => { inTest.value = true progress.value = 0 for (let i = 0; i < tests.length; i++) { - const test = tests[i] + const test = tests[i] ?? '' current.value = test if (test != 'memory' && test != 'disk') { cpu.value[test as keyof typeof cpu.value] = await benchmark.test(test) diff --git a/web/src/views/toolbox/DiskView.vue b/web/src/views/toolbox/DiskView.vue index eeba9eba..e1b253fc 100644 --- a/web/src/views/toolbox/DiskView.vue +++ b/web/src/views/toolbox/DiskView.vue @@ -6,6 +6,7 @@ defineOptions({ import { useRequest } from 'alova/client' import type { DataTableColumns } from 'naive-ui' import { NButton, NProgress, NTag } from 'naive-ui' +import { h } from 'vue' import { useGettext } from 'vue3-gettext' import disk from '@/api/panel/toolbox-disk' diff --git a/web/src/views/toolbox/LogView.vue b/web/src/views/toolbox/LogView.vue index fedac649..9560692d 100644 --- a/web/src/views/toolbox/LogView.vue +++ b/web/src/views/toolbox/LogView.vue @@ -76,24 +76,28 @@ const scanResults = ref>({ // 扫描日志 const handleScan = async (type: string) => { - scanResults.value[type].loading = true - scanResults.value[type].scanned = false - scanResults.value[type].items = [] + const result = scanResults.value[type] + if (!result) return + result.loading = true + result.scanned = false + result.items = [] try { const { data } = await useRequest(toolboxLog.scan(type)) - scanResults.value[type].items = data || [] - scanResults.value[type].scanned = true + result.items = data || [] + result.scanned = true } catch (e) { window.$message.error($gettext('Scan failed')) } finally { - scanResults.value[type].loading = false + result.loading = false } } // 清理日志 const handleClean = async (type: string) => { - scanResults.value[type].cleaning = true + const result = scanResults.value[type] + if (!result) return + result.cleaning = true try { const { data } = await useRequest(toolboxLog.clean(type)) @@ -103,7 +107,7 @@ const handleClean = async (type: string) => { } catch (e) { window.$message.error($gettext('Clean failed')) } finally { - scanResults.value[type].cleaning = false + result.cleaning = false } } @@ -117,7 +121,8 @@ const handleScanAll = async () => { // 清理所有 const handleCleanAll = async () => { for (const logType of logTypes) { - if (scanResults.value[logType.key].items.length > 0) { + const result = scanResults.value[logType.key] + if (result && result.items.length > 0) { await handleClean(logType.key) } } @@ -132,6 +137,11 @@ const totalItems = computed(() => { const anyLoading = computed(() => { return Object.values(scanResults.value).some((r) => r.loading || r.cleaning) }) + +// 获取扫描结果 +const getResult = (key: string): ScanResult => { + return scanResults.value[key] ?? { loading: false, items: [], scanned: false, cleaning: false } +}