diff --git a/frontend/src/api/connection-manager.ts b/frontend/src/api/connection-manager.ts index be832e9..9555028 100644 --- a/frontend/src/api/connection-manager.ts +++ b/frontend/src/api/connection-manager.ts @@ -318,7 +318,8 @@ class ConnectionManagerImpl { try { await this.buildAndPool(profileId, profile) } catch (err) { - this._activeId = prevActiveId + // 回退目标可能已不在池中(死连接重建时 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.notifyChange() throw err diff --git a/frontend/src/components/FileSystem/index.vue b/frontend/src/components/FileSystem/index.vue index b758b53..f6aaa70 100644 --- a/frontend/src/components/FileSystem/index.vue +++ b/frontend/src/components/FileSystem/index.vue @@ -992,6 +992,12 @@ const handlePasteClipboard = async () => { const targetDir = filePath.value if (!targetDir) return + // 剪切源即目标目录时无操作(继续执行会把源文件改名成副本),Explorer 语义是清空剪贴板 + if (clip.mode === 'cut' && clip.fromDir === targetDir) { + clearClipboard() + return + } + let failed = 0 for (const item of clip.items) { const dstName = uniqueNameInList(item.name) diff --git a/internal/oss/aliyun/client.go b/internal/oss/aliyun/client.go index 411e96d..6f5d606 100644 --- a/internal/oss/aliyun/client.go +++ b/internal/oss/aliyun/client.go @@ -489,7 +489,8 @@ func (c *Client) GetSignedURL(ctx context.Context, key string, expiresIn time.Du stringToSign := "GET\n\n\n" + fmt.Sprintf("%d", expiration) + "\n" + path h := hmac.New(sha1.New, []byte(c.config.AccessKeySecret)) h.Write([]byte(stringToSign)) - signature := base64.URLEncoding.EncodeToString(h.Sum(nil)) + // 阿里云签名规范是标准 base64 字母表并保留 padding,= 由下方 QueryEscape 转义 + signature := base64.StdEncoding.EncodeToString(h.Sum(nil)) scheme := "https://" if !c.config.UseHTTPS { diff --git a/internal/sftp/service.go b/internal/sftp/service.go index b5a7149..f13ca5d 100644 --- a/internal/sftp/service.go +++ b/internal/sftp/service.go @@ -632,7 +632,10 @@ func (s *Service) downloadFileTo(c *Client, remotePath, localPath string, copied if err := os.MkdirAll(filepath.Dir(localPath), 0755); err != nil { return fmt.Errorf("创建本地目录失败: %w", err) } + // 重试会整文件重写,进度须回到进入前基线,否则跨重试累计使 copied 超过 total + base := *copied return c.WithRetry(func(sc *sftpclient.Client) error { + *copied = base src, err := sc.Open(remotePath) if err != nil { return err @@ -722,7 +725,10 @@ func (s *Service) UploadFromFile(connID, localPath, remoteDir string, onProgress // uploadFileTo 流式上传单个本地文件(自动创建远端父目录),进度按累计字节回调 func (s *Service) uploadFileTo(c *Client, localPath, remotePath string, copied *int64, total int64, onProgress func(fileName string, copied, total int64)) error { + // 重试会整文件重传,进度须回到进入前基线,否则跨重试累计使 copied 超过 total + base := *copied return c.WithRetry(func(sc *sftpclient.Client) error { + *copied = base if err := sc.MkdirAll(path.Dir(remotePath)); err != nil { return err }