diff --git a/cfg/config.go b/cfg/config.go index bba3b30..67e98c4 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -15,7 +15,7 @@ type Config struct { var T = Config{ DbPath: "./data/data.db", CardDir: "./data", - CardInfo: "info.json", + CardInfo: "package.json", } func init() { diff --git a/global/data.go b/global/data.go index ee7e8fb..176108e 100644 --- a/global/data.go +++ b/global/data.go @@ -17,7 +17,6 @@ func InitDB() { panic(err) } err = DB.AutoMigrate( - &models.ErlangCards{}, &models.ErlangCardCollections{}, &models.History{}, &models.Users{}, diff --git a/global/data_test.go b/global/data_test.go new file mode 100644 index 0000000..24e85ca --- /dev/null +++ b/global/data_test.go @@ -0,0 +1,156 @@ +package global + +import ( + "encoding/json" + "fmt" + "gorm.io/gorm" + "testing" + "work_cation/pkg/gormx" +) + +type TestModel struct { + gorm.Model + Value []byte +} + +func InitDb() *gorm.DB { + const testDbPath = "./test_data.db" + db, err := gormx.New(gormx.Config{DSN: testDbPath, Type: "sqlite3"}) + if err != nil { + panic(err) + } + db.AutoMigrate(&TestModel{}) + return db +} + +func TestP1(t *testing.T) { + db := InitDb() + for i := 0; i < 100; i++ { + db.Create(&TestModel{Value: []byte(fmt.Sprintf("%d", i))}) + } +} + +func TestP2(t *testing.T) { + db := InitDb() + var ( + row []TestModel + id = []int{1, 2} + ) + + f := func(tx *gorm.DB) *gorm.DB { + // Find SELECT `id`,`value` FROM `test_models` WHERE id = 1 AND `test_models`.`deleted_at` IS NULL ORDER BY ID desc + // First SELECT `id`,`value` FROM `test_models` WHERE id = 1 AND `test_models`.`deleted_at` IS NULL ORDER BY ID desc,`test_models`.`id` LIMIT 1 + // row value misused 滥用 + return tx. + Where("id in ?", id). + //Or("id = ? ", id+1). + Select("value"). + Order("ID desc").Find(&row) + } + + testSql(db, f) + fmt.Println("row:", row) + +} + +func TestP3(t *testing.T) { + db := InitDb() + var ( + row []TestModel + //id = []int{1, 2} + ) + + f := func(tx *gorm.DB) *gorm.DB { + // Update `test_models` SET `value`="[1 2]",`updated_at`="2024-10-05 13:21:47.253" WHERE id in (1,2) AND `test_models`.`deleted_at` IS NULL 返回个数:2 err: + // Save SELECT `id`,`value` FROM `test_models` WHERE id = 1 AND `test_models`.`deleted_at` IS NULL ORDER BY ID desc,`test_models`.`id` LIMIT 1 + // row value misused 滥用 + return tx.Model(&TestModel{}). + Where("id in ?", 5). + Save(TestModel{Model: gorm.Model{ID: 5}, Value: []byte("1")}) + //Where("id in ?", id). + //Limit(1). + //Update("value", []byte(fmt.Sprintf("%d", id))) + //Or("id = ? ", id+1). + //Select("value"). + //Order("ID desc").Find(&row) + } + + testSql(db, f) + fmt.Println("row:", row) + +} + +func testSql(db *gorm.DB, fun func(db *gorm.DB) *gorm.DB) { + sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB { + return fun(tx) + }) + + db1 := fun(db) + var sum int64 + err := db1.Count(&sum).Error + + fmt.Printf("接收的sql语句:%s 返回个数:%d err: %v\n", sql, sum, err) + +} + +type KeyValueModel struct { + Key string `gorm:"primarykey"` + Value []byte +} + +func InitMode(name string, db *gorm.DB) error { + return db.Table(name).Migrator().CreateTable(&KeyValueModel{}) +} + +func Get[T any](db *gorm.DB, tableName, key string) *T { + db = db.Table(tableName) + var valueM = KeyValueModel{} + db.Where("key = ?", key).First(&valueM) + var result T + err := json.Unmarshal(valueM.Value, &result) + if err != nil { + return nil + } + return &result +} + +func Set(db *gorm.DB, tableName, key string, value any) error { + var valueM = KeyValueModel{Key: key} + var err error + valueM.Value, err = json.Marshal(value) + if err != nil { + return err + } + var sum int64 + db.Table(tableName).Where("key = ?", key).Count(&sum) + if sum == 0 { + return db.Create(&valueM).Error + } + return db.Table(tableName).Where("key = ?", key).Save(&valueM).Error // ambiguous column name: key +} + +type TestMode1 struct { + Name string + Level int +} + +func TestInitDB1(t *testing.T) { + const tableName = "role1" + db := InitDb() + err := InitMode(tableName, db) + fmt.Println("auto create table:", err) + + err = Set(db, tableName, "1", TestMode1{"测试1", 1}) + if err != nil { + panic(err) + } + ret := Get[TestMode1](db, tableName, "1") + err = Set(db, tableName, "1", TestMode1{"测试1", 2}) + if err != nil { + panic(err) + } + + ret = Get[TestMode1](db, tableName, "1") + fmt.Println(ret) + +} diff --git a/models/BaseCard.go b/models/BaseCard.go index 735fc7e..d625f2b 100644 --- a/models/BaseCard.go +++ b/models/BaseCard.go @@ -20,8 +20,9 @@ type BaseCard struct { } const ( - ToolTypeErlang = "erlangCard" // 卡片 - ToolTypeExecFiles = "execFiles" // 可执行文件 + ToolTypeErlang = "erlangCard" // 卡片 + ToolTypeNoVarErlang = "erlangNoVarCard" + ToolTypeExecFiles = "execFiles" // 可执行文件 ) func NewBaseCard(t, userId string) BaseCard { diff --git a/models/erlangCards.go b/models/erlangCards.go index 1085981..58c40d3 100644 --- a/models/erlangCards.go +++ b/models/erlangCards.go @@ -3,11 +3,7 @@ package models import "work_cation/pkg/gormx" type ErlangCards struct { - UUID string `json:"uuid"` // id ip加时间戳生成 - UserID string `json:"user_id"` // 用户id - Title string `json:"title"` // 标题 - Text string `json:"text"` // 内容 - Covers gormx.ListString `json:"covers"` // 封面 + BaseCard Template string `json:"template"` // 模版 VarName gormx.ListString `json:"var_name"` // 变量 VarContent gormx.ListString `json:"var_content"` // 变量内容 diff --git a/service/baseCardService.go b/service/baseCardService.go index 41dbf98..14a377a 100644 --- a/service/baseCardService.go +++ b/service/baseCardService.go @@ -2,6 +2,9 @@ package service import ( "gorm.io/gorm" + "os" + "path/filepath" + "work_cation/cfg" "work_cation/models" "work_cation/repo" ) @@ -13,3 +16,17 @@ var BaseCard = &BaseCardService{} func (*BaseCardService) Create(db *gorm.DB, baseCard models.BaseCard) { repo.BaseCard.Create(db, &baseCard) } + +func (*BaseCardService) Update(db *gorm.DB, updateCard models.BaseCard) { + +} + +func (*BaseCardService) Delete(db *gorm.DB, deleteCard models.BaseCard) error { + fileDir := filepath.Join(cfg.T.CardDir, deleteCard.UUID) + err := os.RemoveAll(fileDir) + if err != nil { + return err + } + err = db.Model(&models.BaseCard{}).Where("uuid = ?", deleteCard.UUID).Delete(&deleteCard).Error + return err +} diff --git a/service/erlangCard.go b/service/erlangCard.go index 6b06fd6..4c5d9cc 100644 --- a/service/erlangCard.go +++ b/service/erlangCard.go @@ -5,7 +5,6 @@ import ( "gorm.io/gorm" "os" "path/filepath" - "time" "work_cation/cfg" "work_cation/global" "work_cation/models" @@ -17,6 +16,9 @@ type erlangCardService struct{} var ErlangCard = &erlangCardService{} func (*erlangCardService) Create(erlangCard *models.ErlangCards) error { + if len(erlangCard.VarName) == 0 { + erlangCard.ToolType = models.ToolTypeNoVarErlang + } err := os.Mkdir(filepath.Join(cfg.T.CardDir, erlangCard.UUID), os.ModeDir) if err != nil { return err @@ -25,6 +27,7 @@ func (*erlangCardService) Create(erlangCard *models.ErlangCards) error { if err != nil { return err } + defer info.Close() jsonB, err := json.Marshal(erlangCard) if err != nil { return err @@ -33,24 +36,9 @@ func (*erlangCardService) Create(erlangCard *models.ErlangCards) error { if err != nil { return err } - return global.DB.Transaction(func(tx *gorm.DB) error { - err = repo.ErlangCardRepo.Create(global.DB, erlangCard) - if err != nil { - return err - } - var baseCard = models.BaseCard{ - UUID: erlangCard.UUID, - UserID: erlangCard.UserID, - Title: erlangCard.Title, - Text: erlangCard.Text, - Covers: erlangCard.Covers, - ToolType: models.ToolTypeErlang, - UpdateTx: time.Now(), - Number: 0, - Goods: 0, - Collection: 0, - } + var baseCard = erlangCard.BaseCard + err = repo.BaseCard.Create(global.DB, &baseCard) return err }) diff --git a/views/createCards.go b/views/createCards.go index 4cdb95c..d5e36ce 100644 --- a/views/createCards.go +++ b/views/createCards.go @@ -7,23 +7,25 @@ import ( "sort" ) -// CreateCards 创建卡片列表 -func CreateCards(_ fyne.Window) fyne.CanvasObject { +// allCreateCards 创建卡片列表 +func allCreateCards(_ fyne.Window) fyne.CanvasObject { var itemList []fyne.CanvasObject - keys := make([]string, 0, len(CardTypeMap)) - for key, _ := range CardTypeMap { + keys := make([]string, 0, len(cardTypeMap)) + for key, _ := range cardTypeMap { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { - info := CardTypeMap[key] - itemList = append(itemList, widget.NewButton(info.Name, func() { - cWin := fyne.CurrentApp().NewWindow(info.Name) - cWin.SetContent(info.createView(cWin)) - cWin.Resize(info.createSize) - cWin.CenterOnScreen() - cWin.Show() - })) + info := cardTypeMap[key] + if info.createView != nil { + itemList = append(itemList, widget.NewButton(info.Name, func() { + cWin := fyne.CurrentApp().NewWindow(info.Name) + cWin.SetContent(info.createView(cWin)) + cWin.Resize(info.createSize) + cWin.CenterOnScreen() + cWin.Show() + })) + } } return container.NewCenter(container.NewVBox(itemList...)) } diff --git a/views/createView/createErlangCard.go b/views/createView/createErlangCard.go index efd075a..35cf47a 100644 --- a/views/createView/createErlangCard.go +++ b/views/createView/createErlangCard.go @@ -9,23 +9,17 @@ import ( "fyne.io/fyne/v2/widget" "regexp" "strings" + "time" "work_cation/global" "work_cation/models" - "work_cation/pkg/utils" "work_cation/repo" "work_cation/service" ) -var erlangCard *models.ErlangCards - func CreateErlangCard(w fyne.Window) fyne.CanvasObject { - if erlangCard == nil { - erlangCard = &models.ErlangCards{ - UUID: utils.Uuid.CreateUUID(), - UserID: repo.User.GetUserInfo(global.DB).ID, - } - } var ( + // 创建数据 + erlangCard = &models.ErlangCards{BaseCard: models.NewBaseCard(models.ToolTypeErlang, repo.User.GetUserInfo(global.DB).ID)} // 基础表单 formBase = &widget.Form{} // 变量表单 @@ -49,8 +43,7 @@ func CreateErlangCard(w fyne.Window) fyne.CanvasObject { return } dialog.ShowInformation("ok", "创建成功", w) - // 更换 - erlangCard.UUID = utils.Uuid.CreateUUID() + time.AfterFunc(2*time.Second, func() { w.Close() }) }) return container.NewBorder(container.NewHBox(widget.NewLabel("新建"), widget.NewSeparator()), diff --git a/views/data.go b/views/data.go index b2ebbf8..3e56950 100644 --- a/views/data.go +++ b/views/data.go @@ -4,6 +4,7 @@ import ( "fyne.io/fyne/v2" "work_cation/models" "work_cation/views/createView" + "work_cation/views/showView" ) // Tutorial 定义教程的数据结构 @@ -16,9 +17,9 @@ type Tutorial struct { var ( // Tutorials 定义每个教程的元数据 Tutorials = map[string]Tutorial{ - "welcome": {"主页", "", UserViews, true}, - "canvas": {"我的", "", myCardsViews, true}, - "create": {"新建", "", CreateCards, true}, + "welcome": {"主页", "", mainUserViews, true}, + "canvas": {"我的", "", allCardsViews, true}, + "create": {"新建", "", allCreateCards, true}, } // TutorialIndex 定义我们的教程应该如何在索引树中布局 @@ -26,7 +27,7 @@ var ( "": {"welcome", "canvas", "create"}, //"collections": {"list", "table", "tree"}, //"containers": {"apptabs", "border", "box", "center", "doctabs", "grid", "scroll", "split"}, - //"widgets": {"accordion", "button", "card", "entry", "form", "input", "progress", "text", "toolbar"}, + //"widgets": {"accordion", "button", "card", "entry", "form", "input", "progress", "text", "toolbar"}, 58 * 3 + 106 = 280 } ) @@ -34,11 +35,25 @@ type cardInfo struct { Name string createView func(w fyne.Window) fyne.CanvasObject createSize fyne.Size + showView func(fyne.Window, *models.BaseCard) (fyne.CanvasObject, error) } var ( - CardTypeMap = map[string]cardInfo{ - models.ToolTypeErlang: {"Erlang代码脚本模版", createView.CreateErlangCard, fyne.NewSize(600, 400)}, - models.ToolTypeExecFiles: {"自定义可执行工具", createView.CreateExecFile, fyne.NewSize(600, 520)}, + cardTypeMap = map[string]cardInfo{ + models.ToolTypeNoVarErlang: { + "Erlang代码脚本", + nil, + fyne.NewSize(0, 0), + showView.ErlangCardNoVarView}, + models.ToolTypeErlang: { + "Erlang代码脚本模版", + createView.CreateErlangCard, + fyne.NewSize(600, 400), + showView.ErlangCardView}, + models.ToolTypeExecFiles: { + "自定义可执行工具", + createView.CreateExecFile, + fyne.NewSize(600, 520), + showView.ExecFileCardView}, } ) diff --git a/views/showCards.go b/views/showCards.go index cd072b8..ff80cdb 100644 --- a/views/showCards.go +++ b/views/showCards.go @@ -3,24 +3,69 @@ package views import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" "work_cation/global" "work_cation/models" "work_cation/repo" - "work_cation/views/showView" + "work_cation/service" ) -func myCardsViews(w fyne.Window) fyne.CanvasObject { - var items []fyne.CanvasObject +// 卡片展示界面 +func allCardsViews(w fyne.Window) fyne.CanvasObject { + //var items []fyne.CanvasObject + gridWrap := container.NewGridWrap(fyne.NewSize(200, 200)) for _, baseCard := range repo.BaseCard.FindAll(global.DB) { - switch baseCard.ToolType { - case models.ToolTypeErlang: - erlangCard := repo.ErlangCardRepo.Find(global.DB, baseCard.UUID) - items = append(items, showView.ErlangCardView(w, &erlangCard)) - case models.ToolTypeExecFiles: + if info, is := cardTypeMap[baseCard.ToolType]; is { var baseCardCopy = baseCard - items = append(items, showView.ExecFileCardView(w, &baseCardCopy)) + view, err := info.showView(w, &baseCardCopy) + if err != nil { + view = errorCardView(w, &baseCardCopy, err) + } + var baseCardV *widget.Card + baseCardV = baseCardView(w, baseCardV, baseCardCopy, gridWrap, view) + + gridWrap.Add(baseCardV) } } - return container.NewBorder(nil, nil, nil, nil, container.NewScroll( - container.NewGridWrap(fyne.NewSize(200, 200), items...))) + scroll := container.NewScroll(gridWrap) + return container.NewBorder(nil, nil, nil, nil, scroll) +} + +// 单卡界面 +func baseCardView( + w fyne.Window, + baseCardV *widget.Card, + baseCardCopy models.BaseCard, + gridWrap *fyne.Container, + view fyne.CanvasObject) *widget.Card { + + baseCardV = widget.NewCard(baseCardCopy.Title, baseCardCopy.Text, container.NewBorder(nil, widget.NewToolbar( + widget.NewToolbarAction(theme.ContentAddIcon(), func() {}), + widget.NewToolbarAction(theme.DeleteIcon(), func() { + err := service.BaseCard.Delete(global.DB, baseCardCopy) + if err != nil { + dialog.ShowInformation("删除失败", err.Error(), w) + return + } + gridWrap.Remove(baseCardV) + }), + widget.NewToolbarAction(theme.SettingsIcon(), func() {}), + widget.NewToolbarAction(theme.ViewRefreshIcon(), func() { + + }), + ), nil, nil, view)) + return baseCardV +} + +func errorCardView(w fyne.Window, data *models.BaseCard, err error) fyne.CanvasObject { + cardButton := widget.NewButton("出错尝试修复", func() { + dialog.ShowInformation("结果", "修复失败", w) + }) + showErrButton := widget.NewButton("工具解析错误", func() { + dialog.ShowInformation("错误", err.Error(), w) + }) + card := widget.NewCard(data.Title, data.Text, container.NewVBox(showErrButton, cardButton)) + return card } diff --git a/views/showView/erlangCard.go b/views/showView/erlangCard.go index 3acaaf0..5b15235 100644 --- a/views/showView/erlangCard.go +++ b/views/showView/erlangCard.go @@ -1,44 +1,67 @@ package showView import ( + "encoding/json" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/data/binding" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/widget" "github.com/atotto/clipboard" + "os" + "path/filepath" "strings" + "work_cation/cfg" "work_cation/models" ) -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() { - cleanedText := strings.ReplaceAll(data.Template, "\r", "") - cleanedText = strings.ReplaceAll(cleanedText, "\n", "") - err := clipboard.WriteAll(cleanedText) - if err != nil { - dialog.ShowError(err, w) - return - } - dialog.NewInformation("复制脚本成功", data.Template, w).Show() - })) - } - txtBound := binding.NewString() - txtBound.Set(data.Template) - txtWid := widget.NewEntryWithData(txtBound) - - txtWid.Wrapping = fyne.TextWrapOff - cardButton := widget.NewButton("OPEN", func() { - go UseErlangCard(data) +func ErlangCardNoVarView(w fyne.Window, baseInfo *models.BaseCard) (fyne.CanvasObject, error) { + button := widget.NewButton("复制", func() { + // 读取类型文件数据 + infoPath := filepath.Join(cfg.T.CardDir, baseInfo.UUID, cfg.T.CardInfo) + bytes, err := os.ReadFile(infoPath) + if err != nil { + dialog.ShowError(err, w) + return + } + var data models.ErlangCards + err = json.Unmarshal(bytes, &data) + if err != nil { + dialog.ShowError(err, w) + return + } + cleanedText := strings.ReplaceAll(data.Template, "\r", "") + cleanedText = strings.ReplaceAll(cleanedText, "\n", "") + err = clipboard.WriteAll(cleanedText) + if err != nil { + dialog.ShowError(err, w) + return + } + dialog.NewInformation("复制脚本成功", data.Template, w).Show() }) + return button, nil +} - card := widget.NewCard(data.Title, data.Text, cardButton) - +func ErlangCardView(w fyne.Window, baseInfo *models.BaseCard) (fyne.CanvasObject, error) { + cardButton := widget.NewButton("OPEN", func() { + // 读取类型文件数据 + infoPath := filepath.Join(cfg.T.CardDir, baseInfo.UUID, cfg.T.CardInfo) + bytes, err := os.ReadFile(infoPath) + if err != nil { + dialog.ShowError(err, w) + return + } + var data models.ErlangCards + err = json.Unmarshal(bytes, &data) + if err != nil { + dialog.ShowError(err, w) + return + } + go UseErlangCard(&data) + }) //image := canvas.NewImageFromResource(assets.LogoDataSR) //image.FillMode = canvas.ImageFillContain //card.SetImage(image) - return card + return cardButton, nil } func UseErlangCard(data *models.ErlangCards) { diff --git a/views/showView/execFile.go b/views/showView/execFile.go index 59466ed..dfdb927 100644 --- a/views/showView/execFile.go +++ b/views/showView/execFile.go @@ -13,12 +13,12 @@ import ( "work_cation/models" ) -func ExecFileCardView(w fyne.Window, data *models.BaseCard) fyne.CanvasObject { +func ExecFileCardView(w fyne.Window, data *models.BaseCard) (fyne.CanvasObject, error) { 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 + card := container.NewVBox(cardButton, openDirButton) + return card, nil } func runExecFile(data *models.BaseCard, w fyne.Window) { diff --git a/views/userView.go b/views/userView.go index e81cc98..49130f6 100644 --- a/views/userView.go +++ b/views/userView.go @@ -10,7 +10,7 @@ import ( "work_cation/repo" ) -func UserViews(w fyne.Window) fyne.CanvasObject { +func mainUserViews(w fyne.Window) fyne.CanvasObject { image := canvas.NewImageFromResource(assets.LogoDataSR) image.FillMode = canvas.ImageFillContain