From 1a0a9466d8ec037fe6709fd6607b81bde312f2b2 Mon Sep 17 00:00:00 2001 From: shine <1042864399@qq.com> Date: Thu, 10 Oct 2024 18:22:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=93=E5=B1=95=E7=B1=BB=E5=9E=8B=20?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=B7=A5=E5=85=B7=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 + go.sum | 4 ++ models/BaseCard.go | 15 ++++- models/execFiles.go | 9 +++ pkg/utils/exec.go | 51 ++++++++++++++++ pkg/utils/file.go | 95 ++++++++++++++++++++++++++++++ service/baseCardService.go | 15 +++++ service/execFile.go | 54 +++++++++++++++++ views/createCards.go | 3 +- views/createView/createExecFile.go | 76 +++++++++++++++++------- views/data.go | 6 +- views/showCards.go | 3 + views/showView/erlangCard.go | 9 ++- views/showView/execFile.go | 63 ++++++++++++++++++++ 14 files changed, 374 insertions(+), 31 deletions(-) create mode 100644 models/execFiles.go create mode 100644 pkg/utils/exec.go create mode 100644 pkg/utils/file.go create mode 100644 service/baseCardService.go create mode 100644 service/execFile.go create mode 100644 views/showView/execFile.go diff --git a/go.mod b/go.mod index 01a72c5..6c4b2be 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( require ( fyne.io/systray v1.11.0 // indirect github.com/BurntSushi/toml v1.4.0 // indirect + github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fredbi/uri v1.1.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -41,6 +42,7 @@ require ( github.com/pkg/errors v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rymdport/portal v0.2.6 // indirect + github.com/sqweek/dialog v0.0.0-20240226140203-065105509627 // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect github.com/stretchr/testify v1.8.4 // indirect diff --git a/go.sum b/go.sum index c9717fb..25c612f 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf h1:FPsprx82rdrX2jiKyS17BH6IrTmUBYqZa/CXT4uvb+I= +github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -287,6 +289,8 @@ github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t6 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/sqweek/dialog v0.0.0-20240226140203-065105509627 h1:2JL2wmHXWIAxDofCK+AdkFi1KEg3dgkefCsm7isADzQ= +github.com/sqweek/dialog v0.0.0-20240226140203-065105509627/go.mod h1:/qNPSY91qTz/8TgHEMioAUc6q7+3SOybeKczHMXFcXw= github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE= github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= diff --git a/models/BaseCard.go b/models/BaseCard.go index 65f7ffb..735fc7e 100644 --- a/models/BaseCard.go +++ b/models/BaseCard.go @@ -3,6 +3,7 @@ package models import ( "time" "work_cation/pkg/gormx" + "work_cation/pkg/utils" ) type BaseCard struct { @@ -19,5 +20,17 @@ type BaseCard struct { } const ( - ToolTypeErlang = "erlangCard" // 卡片 + ToolTypeErlang = "erlangCard" // 卡片 + ToolTypeExecFiles = "execFiles" // 可执行文件 ) + +func NewBaseCard(t, userId string) BaseCard { + return BaseCard{ + UUID: utils.Uuid.CreateUUID(), + UserID: userId, + Title: "未命名", + Text: "未命名", + ToolType: t, + UpdateTx: time.Now(), + } +} diff --git a/models/execFiles.go b/models/execFiles.go new file mode 100644 index 0000000..f66157e --- /dev/null +++ b/models/execFiles.go @@ -0,0 +1,9 @@ +package models + +type ExecFiles struct { + BaseCard + // 文件名 + Cmd string `json:"common"` // 执行命令 + Pwd string `json:"pwd"` // 执行路径 + Files []string `json:"files"` // 原始文件 +} diff --git a/pkg/utils/exec.go b/pkg/utils/exec.go new file mode 100644 index 0000000..51e8112 --- /dev/null +++ b/pkg/utils/exec.go @@ -0,0 +1,51 @@ +package utils + +import ( + "bufio" + "context" + "io" + "os/exec" + "strings" + "syscall" +) + +type execUtils struct{} + +var Exec = &execUtils{} + +func (e *execUtils) Command(cmdstr, dir string) (string, error) { + reader, err := e.CommandContext(context.Background(), cmdstr, dir, nil) + if err != nil { + return "", err + } + scanner := bufio.NewScanner(reader) + var out string + for scanner.Scan() { + out += scanner.Text() + "\n" + } + return out, reader.Close() +} + +func (e *execUtils) CommandContext(ctx context.Context, cmdstr, dir string, cancel func()) (io.ReadCloser, error) { + cmdArgs := strings.Split(cmdstr, " ") + cmd := exec.CommandContext(ctx, cmdArgs[0], cmdArgs[1:]...) + cmd.Dir = dir + cmd.Cancel = func() error { + if cancel != nil { + cancel() + } + return cmd.Process.Kill() + } + // 关闭黑框 + cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} + // 输出 + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, err + } + err = cmd.Start() + if err != nil { + return nil, err + } + return stdout, nil +} diff --git a/pkg/utils/file.go b/pkg/utils/file.go new file mode 100644 index 0000000..1c1c539 --- /dev/null +++ b/pkg/utils/file.go @@ -0,0 +1,95 @@ +package utils + +import ( + "io" + "os" + "os/exec" + "path/filepath" +) + +type FileUtils struct{} + +var File = &FileUtils{} + +func (f *FileUtils) CopyDir(src, dst string) error { + // 获取源文件夹信息 + srcInfo, err := os.Stat(src) + if err != nil { + return err + } + + // 创建目标文件夹 + err = os.MkdirAll(dst, srcInfo.Mode()) + if err != nil { + return err + } + + // 读取源文件夹 + dir, err := os.ReadDir(src) + if err != nil { + return err + } + + for _, file := range dir { + srcFile := filepath.Join(src, file.Name()) + dstFile := filepath.Join(dst, file.Name()) + + if file.IsDir() { + // 递归复制子文件夹 + err = f.CopyDir(srcFile, dstFile) + if err != nil { + return err + } + } else { + // 复制文件 + err = f.CopyFile(srcFile, dstFile) + if err != nil { + return err + } + } + } + + return nil +} + +func (f *FileUtils) CopyFile(src, dst string) error { + srcFile, err := os.Open(src) + if err != nil { + return err + } + defer srcFile.Close() + + dstFile, err := os.Create(dst) + if err != nil { + return err + } + defer dstFile.Close() + + _, err = io.Copy(dstFile, srcFile) + if err != nil { + return err + } + return nil +} + +func (*FileUtils) WinOpenFolder(folderPath string) error { + cmd := exec.Command("explorer", folderPath) + return cmd.Run() + //fileInfo, err := os.Stat(folderPath) + //if os.IsNotExist(err) { + // return err + //} + //var ( + // dir = "" + // f = "" + //) + //if fileInfo.IsDir() { + // dir = "" + // f = folderPath + //} else { + // dir = filepath.Dir(folderPath) + // f = filepath.Base(folderPath) + //} + //_, err = Exec.Command(fmt.Sprintf("explorer %s", f), dir) + //return err +} diff --git a/service/baseCardService.go b/service/baseCardService.go new file mode 100644 index 0000000..41dbf98 --- /dev/null +++ b/service/baseCardService.go @@ -0,0 +1,15 @@ +package service + +import ( + "gorm.io/gorm" + "work_cation/models" + "work_cation/repo" +) + +type BaseCardService struct{} + +var BaseCard = &BaseCardService{} + +func (*BaseCardService) Create(db *gorm.DB, baseCard models.BaseCard) { + repo.BaseCard.Create(db, &baseCard) +} diff --git a/service/execFile.go b/service/execFile.go new file mode 100644 index 0000000..3457f1d --- /dev/null +++ b/service/execFile.go @@ -0,0 +1,54 @@ +package service + +import ( + "encoding/json" + "gorm.io/gorm" + "os" + "path/filepath" + "work_cation/cfg" + "work_cation/global" + "work_cation/models" + "work_cation/pkg/utils" + "work_cation/repo" +) + +type ExecFileService struct{} + +var ExecFile = new(ExecFileService) + +func (*ExecFileService) Create(info *models.ExecFiles, chooseDir string) error { + infoPath := filepath.Join(cfg.T.CardDir, info.UUID) + //if err := os.RemoveAll(infoPath); err != nil && !os.IsNotExist(err) { + // dialog.ShowInformation("删除失败", err.Error(), w) + // return + //} + if err := utils.File.CopyDir(chooseDir, infoPath); err != nil { + return err + } + + fs, err := os.ReadDir(chooseDir) + if err != nil { + return err + } + for _, f := range fs { + info.Files = append(info.Files, f.Name()) + } + + jsonB, err := json.Marshal(info) + if err != nil { + return err + } + file, err := os.Create(filepath.Join(infoPath, cfg.T.CardInfo)) + if err != nil { + return err + } + defer file.Close() + _, err = file.Write(jsonB) + if err != nil { + return err + } + return global.DB.Transaction(func(tx *gorm.DB) error { + var baseCard = info.BaseCard + return repo.BaseCard.Create(tx, &baseCard) + }) +} diff --git a/views/createCards.go b/views/createCards.go index fa65ae4..4cdb95c 100644 --- a/views/createCards.go +++ b/views/createCards.go @@ -8,7 +8,7 @@ import ( ) // CreateCards 创建卡片列表 -func CreateCards(w fyne.Window) fyne.CanvasObject { +func CreateCards(_ fyne.Window) fyne.CanvasObject { var itemList []fyne.CanvasObject keys := make([]string, 0, len(CardTypeMap)) for key, _ := range CardTypeMap { @@ -18,7 +18,6 @@ func CreateCards(w fyne.Window) fyne.CanvasObject { for _, key := range keys { info := CardTypeMap[key] itemList = append(itemList, widget.NewButton(info.Name, func() { - info := CardTypeMap[key] cWin := fyne.CurrentApp().NewWindow(info.Name) cWin.SetContent(info.createView(cWin)) cWin.Resize(info.createSize) diff --git a/views/createView/createExecFile.go b/views/createView/createExecFile.go index a2aaa6f..b9de93a 100644 --- a/views/createView/createExecFile.go +++ b/views/createView/createExecFile.go @@ -1,57 +1,91 @@ package createView import ( + "fmt" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/widget" + dialogWin "github.com/sqweek/dialog" + "log" + "os/exec" + "path/filepath" + "time" "work_cation/global" "work_cation/models" - "work_cation/pkg/utils" "work_cation/repo" "work_cation/service" ) +var chooseDir string + func CreateExecFile(w fyne.Window) fyne.CanvasObject { - if erlangCard == nil { - erlangCard = &models.ErlangCards{ - UUID: utils.Uuid.CreateUUID(), - UserID: repo.User.GetUserInfo(global.DB).ID, - } - } var ( // 基础表单 formBase = &widget.Form{} - // 变量表单 - formVars = &widget.Form{} + info = &models.ExecFiles{BaseCard: models.NewBaseCard( + models.ToolTypeExecFiles, + repo.User.GetUserInfo(global.DB).ID, + )} ) - formBase.AppendItem(&widget.FormItem{Text: "名称", Widget: newDefaultEntry(&erlangCard.Title)}) - formBase.AppendItem(&widget.FormItem{Text: "描述", Widget: newDefaultEntry(&erlangCard.Text)}) - formBase.AppendItem(&widget.FormItem{Text: "封面", Widget: newDefaultEntry(&erlangCard.Covers), HintText: "暂未支持可不填"}) - formBase.AppendItem(&widget.FormItem{Text: "是否默认展示", Widget: newDefaultEntry(&erlangCard.IsShowOut)}) - formBase.AppendItem(&widget.FormItem{Text: "模版内容", Widget: newDefaultEntryCallback(&erlangCard.Template, func() { generateVariableForm1(erlangCard, formVars) })}) + formBase.AppendItem(&widget.FormItem{Text: "名称", Widget: newDefaultEntry(&info.Title)}) + formBase.AppendItem(&widget.FormItem{Text: "描述", Widget: newDefaultEntry(&info.Text)}) + formBase.AppendItem(&widget.FormItem{Text: "封面", Widget: newDefaultEntry(&info.Covers), HintText: "暂未支持可不填"}) - // 刷新变量表单 - generateVariableForm1(erlangCard, formVars) + formBase.AppendItem(&widget.FormItem{Text: "执行命令", Widget: newDefaultEntry(&info.Cmd), HintText: "shell 命令 例: ./数据回档.exe"}) + formBase.AppendItem(&widget.FormItem{Text: "执行路径", Widget: newDefaultEntry(&info.Pwd), HintText: ". 默认路径"}) + + chooseDirEntry := newDefaultEntry(&chooseDir) + formBase.AppendItem(&widget.FormItem{Text: "文件夹", Widget: chooseDirEntry}) + + buttonDir := widget.NewButton("选择路径", func() { + chooseDir1, err := dialogWin.Directory().Title("选择工具所在文件夹").SetStartDir(chooseDir).Browse() + if err != nil { + return + } + chooseDirEntry.SetText(chooseDir1) + log.Println("Selected folder:", chooseDir) + + }) // 保存按钮 buttonSave := widget.NewButton("保存", func() { - err := service.ErlangCard.Create(erlangCard) + fmt.Println("info:", info) + err := service.ExecFile.Create(info, chooseDir) if err != nil { - dialog.ShowError(err, w) + dialog.ShowInformation("创建失败", err.Error(), w) return } dialog.ShowInformation("ok", "创建成功", w) // 更换 - erlangCard.UUID = utils.Uuid.CreateUUID() + time.AfterFunc(2*time.Second, func() { w.Close() }) + }) + + buttonTest := widget.NewButton("测试", func() { + if info.Cmd == "" { + dialog.ShowInformation("测试失败", "未填写执行命令", w) + return + } + ex := exec.Command(info.Cmd) + ex.Dir = filepath.Join(chooseDir, info.Pwd) + if err := ex.Start(); err != nil { + dialog.ShowInformation("测试失败", err.Error(), w) + return + } + go func() { + if err := ex.Wait(); err != nil { + dialog.ShowInformation("测试失败", err.Error(), w) + } + }() }) return container.NewBorder(container.NewHBox(widget.NewLabel("新建"), widget.NewSeparator()), nil, nil, nil, container.NewScroll( container.NewVBox( formBase, - formVars, - buttonSave, + buttonDir, + buttonTest, + container.NewHBox(buttonSave), ))) } diff --git a/views/data.go b/views/data.go index ec4db82..b2ebbf8 100644 --- a/views/data.go +++ b/views/data.go @@ -38,9 +38,7 @@ type cardInfo struct { var ( CardTypeMap = map[string]cardInfo{ - models.ToolTypeErlang: {"erlang代码脚本1", createView.CreateErlangCard, fyne.NewSize(600, 400)}, - "2": {"erlang代码脚本2", createView.CreateErlangCard, fyne.NewSize(600, 400)}, - "3": {"erlang代码脚本3", createView.CreateErlangCard, fyne.NewSize(600, 400)}, - "4": {"erlang代码脚本4", createView.CreateErlangCard, fyne.NewSize(600, 400)}, + models.ToolTypeErlang: {"Erlang代码脚本模版", createView.CreateErlangCard, fyne.NewSize(600, 400)}, + models.ToolTypeExecFiles: {"自定义可执行工具", createView.CreateExecFile, fyne.NewSize(600, 520)}, } ) diff --git a/views/showCards.go b/views/showCards.go index 3a2d3f8..cd072b8 100644 --- a/views/showCards.go +++ b/views/showCards.go @@ -16,6 +16,9 @@ func myCardsViews(w fyne.Window) fyne.CanvasObject { case models.ToolTypeErlang: erlangCard := repo.ErlangCardRepo.Find(global.DB, baseCard.UUID) items = append(items, showView.ErlangCardView(w, &erlangCard)) + case models.ToolTypeExecFiles: + var baseCardCopy = baseCard + items = append(items, showView.ExecFileCardView(w, &baseCardCopy)) } } return container.NewBorder(nil, nil, nil, nil, container.NewScroll( diff --git a/views/showView/erlangCard.go b/views/showView/erlangCard.go index 31cd0ca..3acaaf0 100644 --- a/views/showView/erlangCard.go +++ b/views/showView/erlangCard.go @@ -14,7 +14,9 @@ import ( func ErlangCardView(w fyne.Window, data *models.ErlangCards) fyne.CanvasObject { if len(data.VarName) == 0 { return widget.NewCard(data.Title, data.Text, widget.NewButton("复制", func() { - err := clipboard.WriteAll(data.Template) + cleanedText := strings.ReplaceAll(data.Template, "\r", "") + cleanedText = strings.ReplaceAll(cleanedText, "\n", "") + err := clipboard.WriteAll(cleanedText) if err != nil { dialog.ShowError(err, w) return @@ -121,6 +123,7 @@ func replaceVars(input string, replacements []string) string { result = strings.Replace(result, "$var", replacements[varIndex], 1) varIndex++ } - - return result + cleanedText := strings.ReplaceAll(result, "\r", "") + cleanedText = strings.ReplaceAll(cleanedText, "\n", "") + return cleanedText } diff --git a/views/showView/execFile.go b/views/showView/execFile.go new file mode 100644 index 0000000..59466ed --- /dev/null +++ b/views/showView/execFile.go @@ -0,0 +1,63 @@ +package showView + +import ( + "encoding/json" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/widget" + "os" + "os/exec" + "path/filepath" + "work_cation/cfg" + "work_cation/models" +) + +func ExecFileCardView(w fyne.Window, data *models.BaseCard) fyne.CanvasObject { + cardButton := widget.NewButton("启动", func() { runExecFile(data, w) }) + openDirButton := widget.NewButton("打开目录", func() { openDir(data, w) }) + + card := widget.NewCard(data.Title, data.Text, container.NewVBox(cardButton, openDirButton)) + return card +} + +func runExecFile(data *models.BaseCard, w fyne.Window) { + infoPath := filepath.Join(cfg.T.CardDir, data.UUID, cfg.T.CardInfo) + bytes, err := os.ReadFile(infoPath) + if err != nil { + dialog.ShowInformation("测试失败", err.Error(), w) + return + } + var info models.ExecFiles + err = json.Unmarshal(bytes, &info) + if err != nil { + dialog.ShowInformation("测试失败", err.Error(), w) + return + } + ex := exec.Command(info.Cmd) + ex.Dir = filepath.Join(cfg.T.CardDir, data.UUID, info.Pwd) + if err := ex.Start(); err != nil { + dialog.ShowInformation("测试失败", err.Error(), w) + return + } + go func() { + if err := ex.Wait(); err != nil { + dialog.ShowInformation("测试失败", err.Error(), w) + } + }() +} + +func openDir(data *models.BaseCard, w fyne.Window) { + infoPath := filepath.Join(cfg.T.CardDir, data.UUID) + // 打开目录 + //utils.File.WinOpenFolder(infoPath) + cmd := exec.Command("explorer", infoPath) + if err := cmd.Start(); err != nil { + dialog.ShowInformation("打开失败", err.Error(), w) + } + go func() { + if err := cmd.Wait(); err != nil && err.Error() != "exit status 1" { + dialog.ShowInformation("测试失败", err.Error(), w) + } + }() +}