修复: 文件系统初始化守卫与传输收尾等七处

- filesystemSvc 守卫17绑定方法,异步初始化完成前返回明确错误
- MovePath 跨盘剪切建 move emitter,失败清理目标盘残留
- SFTP 空目录下载/上传补 MkdirAll 落地
- drawio 下载失败等待者立即返回,不再空轮询30分钟
- batch 高亮补 %1 与 %%i 规则
- 错误文案转换抽 errMessage 共享函数,11处归一
- log.Printf 统一 slog.Warn
This commit is contained in:
lxy
2026-09-15 23:49:01 +08:00
parent d211365856
commit 56fe0d9b82
10 changed files with 173 additions and 49 deletions
+7 -11
View File
@@ -9,6 +9,7 @@ import { HttpTransport } from './http-transport'
import { SftpTransport } from './sftp-transport'
import { OssTransport } from './oss-transport'
import { getFileServerBaseURL } from './file-server'
import { errMessage } from '@/utils/errors'
import {
LoadConnectionProfiles, SaveConnectionProfile, DeleteConnectionProfile,
SftpGetSystemInfo, GetLocalSystemInfo,
@@ -268,7 +269,7 @@ class ConnectionManagerImpl {
}
}
}
} catch (e) { info._error = true; info._errorMsg = errMsg(e) }
} catch (e) { info._error = true; info._errorMsg = errMessage(e) }
this._sysInfoCache.set(profileId, info)
this._sysInfoChangeCallbacks.forEach(cb => cb(profileId, info))
@@ -320,7 +321,7 @@ class ConnectionManagerImpl {
} catch (err) {
// 回退目标可能已不在池中(死连接重建时 disconnectProfile 先移除),指向池外会让 getTransport 静默回落本地造成 UI 幻影
this._activeId = (prevActiveId && this._pool.has(prevActiveId)) ? prevActiveId : 'local-default'
this.setProfileState(profileId, 'error', err instanceof Error ? err.message : String(err))
this.setProfileState(profileId, 'error', errMessage(err))
this.notifyChange()
throw err
}
@@ -433,7 +434,7 @@ class ConnectionManagerImpl {
this.fetchSystemInfo(profileId).catch(() => {})
} catch (err) {
this._pool.delete(profileId)
this.setProfileState(profileId, 'error', errMsg(err))
this.setProfileState(profileId, 'error', errMessage(err))
this.setState('error')
throw err
}
@@ -453,7 +454,7 @@ class ConnectionManagerImpl {
this.updateProfile(profileId, { lastConnected: Date.now() })
} catch (err) {
this._pool.delete(profileId)
this.setProfileState(profileId, 'error', errMsg(err))
this.setProfileState(profileId, 'error', errMessage(err))
this.setState('error')
throw err
}
@@ -474,13 +475,13 @@ class ConnectionManagerImpl {
this.updateProfile(profileId, { lastConnected: Date.now() })
this.fetchSystemInfo(profileId).catch(() => {})
}).catch((e) => {
this.setProfileState(profileId, 'error', errMsg(e))
this.setProfileState(profileId, 'error', errMessage(e))
if (this._activeId !== profileId) return
this.setState('error')
})
} catch (err) {
this._pool.delete(profileId)
this.setProfileState(profileId, 'error', errMsg(err))
this.setProfileState(profileId, 'error', errMessage(err))
this.setState('error')
throw err
}
@@ -489,11 +490,6 @@ class ConnectionManagerImpl {
export const connectionManager = new ConnectionManagerImpl()
/** 任意抛出值转错误文案 */
function errMsg(e: unknown): string {
return e instanceof Error ? e.message : String(e)
}
/** snake_case → camelCase */
function snakeToCamel(obj: Record<string, any>): Record<string, any> {
const result: Record<string, any> = {}
+6 -5
View File
@@ -155,6 +155,7 @@ import { connectionManager } from '@/api/connection-manager'
import { STORAGE_KEYS, DEFAULTS, UI_TEXT, VALIDATION_RULES, FILE_EXTENSIONS, FILE_SIZE_THRESHOLDS } from '@/utils/constants'
import { isAutoRestoreDisabled, clearAutoRestoreDisabled } from '@/utils/freezeWatchdog'
import { parseWailsEventData } from '@/utils/wailsEvent'
import { errMessage } from '@/utils/errors'
import { createResizeHandler } from '@/utils/resize'
// 导入类型
@@ -524,7 +525,7 @@ const handleConnectionChanged = async (profileId: string) => {
try {
await switchConnection(profileId)
} catch (err) {
Message.error(`连接失败: ${err instanceof Error ? err.message : String(err)}`)
Message.error(`连接失败: ${errMessage(err)}`)
}
}
@@ -647,7 +648,7 @@ const handleOpenFavorite = async (file: FavoriteFile) => {
await loadCommonPaths()
} catch (e) {
console.error('切换连接失败:', e)
Message.error(`无法连接: ${e instanceof Error ? e.message : String(e)}`)
Message.error(`无法连接: ${errMessage(e)}`)
return
}
} else if (!file.profileId) {
@@ -1011,7 +1012,7 @@ const handlePasteClipboard = async () => {
} catch (error) {
failed++
console.error('[Clipboard] 粘贴失败:', item.path, error)
Message.error(`粘贴失败: ${item.name} - ${error instanceof Error ? error.message : error}`)
Message.error(`粘贴失败: ${item.name} - ${errMessage(error)}`)
}
}
@@ -1033,7 +1034,7 @@ const handleDownloadToLocal = async (file: FileItem) => {
const localPath = await connectionManager.getTransport().downloadToFile(file.path, dstDir)
Message.success(`已下载到: ${localPath}`)
} catch (error) {
Message.error(`下载失败: ${error instanceof Error ? error.message : error}`)
Message.error(`下载失败: ${errMessage(error)}`)
}
}
@@ -1067,7 +1068,7 @@ const handleExternalDrop = async (paths: string[]) => {
Message.success(`已复制 ${paths.length} 个项目`)
}
} catch (error) {
Message.error(`拖入失败: ${error instanceof Error ? error.message : error}`)
Message.error(`拖入失败: ${errMessage(error)}`)
await loadDirectory(targetDir)
}
}
+2 -2
View File
@@ -108,8 +108,8 @@ export async function loadLanguageExtension(language) {
if (stream.eatSpace()) return null
if (stream.match(/^"(?:[^"\r\n]|"")*"?/)) return 'string'
if (stream.eat('^')) return 'operator'
// !var! 延迟变量、%var% 环境变量、%~d0 类批处理参数
if (stream.match(/^!\w+!/) || stream.match(/^%\w+%/) || stream.match(/^%~[a-z]*[\d*]/i)) return 'atom'
// !var! 延迟变量、%var% 环境变量、%~d0 类批处理参数、%1 位置参数、%%i for 循环变量
if (stream.match(/^!\w+!/) || stream.match(/^%\w+%/) || stream.match(/^%~[a-z]*[\d*]/i) || stream.match(/^%\d/) || stream.match(/^%%\w+/)) return 'atom'
if (stream.match(/^\w+/)) {
return batchKeywords.has(stream.current().toLowerCase()) ? 'keyword' : null
}
+7
View File
@@ -0,0 +1,7 @@
/**
* 任意抛出值转错误文案(Error 取 message,其余 String 化)
*/
export function errMessage(e: unknown): string {
return e instanceof Error ? e.message : String(e)
}