新增: 文件操作增强与预览扩展
- 文件剪贴板复制剪切粘贴,重名自动副本 - SFTP下载到本机与外部文件拖入,目录递归 - 传输进度浮动面板,transfer-progress事件链 - Drawio预览,Excel工作线程,冻结看门狗 - cmd与bat自写batch语法高亮,psm1等映射补齐
This commit is contained in:
+115
-1
@@ -3,10 +3,31 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"u-desk/internal/filesystem"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
// EnsureDrawioViewer 确保本地已缓存 drawio viewer(首次预览时下载),前端打开 .drawio 前调用。
|
||||
// 通过 drawio-download-progress 事件推送下载进度。
|
||||
func (a *App) EnsureDrawioViewer() (map[string]interface{}, error) {
|
||||
err := filesystem.EnsureDrawioViewer(func(progress, speed float64, downloaded, total int64) {
|
||||
a.mainWindow.EmitEvent("drawio-download-progress", map[string]interface{}{
|
||||
"progress": progress,
|
||||
"speed": speed,
|
||||
"downloaded": downloaded,
|
||||
"total": total,
|
||||
})
|
||||
})
|
||||
return map[string]interface{}{
|
||||
"ready": err == nil,
|
||||
"version": filesystem.DrawioViewerVersion(),
|
||||
}, err
|
||||
}
|
||||
|
||||
// ReadFile 读取文件
|
||||
func (a *App) ReadFile(path string) (string, error) {
|
||||
return a.filesystem.ReadFile(path)
|
||||
@@ -62,6 +83,98 @@ func (a *App) RenamePath(req RenamePathRequest) (*filesystem.FileOperationResult
|
||||
return a.filesystem.RenamePath(req.OldPath, req.NewPath)
|
||||
}
|
||||
|
||||
// CopyPath 复制文件或目录(递归),通过 transfer-progress 事件推送进度
|
||||
func (a *App) CopyPath(src, dst string) error {
|
||||
emitter := newTransferEmitter(a, "copy", filepath.Base(filepath.FromSlash(src)))
|
||||
err := a.filesystem.CopyPath(src, dst, func(copied, total int64) {
|
||||
emitter.progress(emitter.name, copied, total)
|
||||
})
|
||||
emitter.finish(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// MovePath 移动文件或目录(同盘重命名,跨盘复制后删除)
|
||||
func (a *App) MovePath(src, dst string) error {
|
||||
return a.filesystem.MovePath(src, dst)
|
||||
}
|
||||
|
||||
// PickDirectory 打开系统目录选择器,返回选中目录(取消时返回空串)
|
||||
func (a *App) PickDirectory() (string, error) {
|
||||
dialog := application.Get().Dialog.OpenFileWithOptions(&application.OpenFileDialogOptions{
|
||||
CanChooseDirectories: true,
|
||||
CanChooseFiles: false,
|
||||
CanCreateDirectories: true,
|
||||
Title: "选择目录",
|
||||
})
|
||||
return dialog.PromptForSingleSelection()
|
||||
}
|
||||
|
||||
// ========== 传输进度事件(下载/上传/本地复制共用) ==========
|
||||
|
||||
// transferProgressInterval 进度事件最小推送间隔(节流)
|
||||
const transferProgressInterval = 100 * time.Millisecond
|
||||
|
||||
// transferEmitter 封装 transfer-progress 事件的节流推送
|
||||
type transferEmitter struct {
|
||||
app *App
|
||||
id string
|
||||
name string
|
||||
lastEmit time.Time
|
||||
}
|
||||
|
||||
// newTransferID 生成传输任务标识
|
||||
func newTransferID(prefix string) string {
|
||||
return fmt.Sprintf("%s-%d", prefix, time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// newTransferEmitter 创建进度推送器并发送首帧 running 事件
|
||||
func newTransferEmitter(a *App, prefix, name string) *transferEmitter {
|
||||
te := &transferEmitter{app: a, id: newTransferID(prefix), name: name}
|
||||
te.emit(name, 0, 0, "")
|
||||
return te
|
||||
}
|
||||
|
||||
// emit 推送一帧进度(errMsg 非空表示失败)
|
||||
func (t *transferEmitter) emit(name string, copied, total int64, errMsg string) {
|
||||
payload := map[string]interface{}{
|
||||
"id": t.id,
|
||||
"name": name,
|
||||
"copied": copied,
|
||||
"total": total,
|
||||
"status": "running",
|
||||
}
|
||||
if errMsg != "" {
|
||||
payload["status"] = "error"
|
||||
payload["error"] = errMsg
|
||||
}
|
||||
t.app.emitEvent("transfer-progress", payload)
|
||||
}
|
||||
|
||||
// progress 流式进度回调(100ms 节流;目录传输时 name 为当前文件名)
|
||||
func (t *transferEmitter) progress(name string, copied, total int64) {
|
||||
t.name = name
|
||||
now := time.Now()
|
||||
if now.Sub(t.lastEmit) < transferProgressInterval {
|
||||
return
|
||||
}
|
||||
t.lastEmit = now
|
||||
t.emit(name, copied, total, "")
|
||||
}
|
||||
|
||||
// finish 结束传输:成功发 done,失败发 error(各一帧,不节流)
|
||||
func (t *transferEmitter) finish(err error) {
|
||||
if err != nil {
|
||||
t.emit(t.name, 0, 0, err.Error())
|
||||
return
|
||||
}
|
||||
payload := map[string]interface{}{
|
||||
"id": t.id,
|
||||
"name": t.name,
|
||||
"status": "done",
|
||||
}
|
||||
t.app.emitEvent("transfer-progress", payload)
|
||||
}
|
||||
|
||||
// GetFileInfo 获取文件信息
|
||||
func (a *App) GetFileInfo(path string) (map[string]interface{}, error) {
|
||||
return a.filesystem.GetFileInfo(path)
|
||||
@@ -161,7 +274,8 @@ func (a *App) initFilesystemModule() {
|
||||
return
|
||||
}
|
||||
|
||||
go a.startFileServer()
|
||||
// startFileServer 内部 listen 同步(极快)、Serve 异步,无需再包 goroutine
|
||||
a.startFileServer()
|
||||
|
||||
slog.Info("文件系统模块初始化完成")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user