24 Commits

Author SHA1 Message Date
sutong 4ec023e1c2 修复游戏对象id错误导致的同步问题 2024-10-19 20:39:12 +08:00
sutong 83dfff5aa4 完善小游戏逻辑 2024-10-18 22:42:58 +08:00
sutong 8a8a6e0bfa 修复了聊天数据异常丢失的bug 2024-10-18 18:15:44 +08:00
sutong b27d2b80f7 网络游戏初步接入 2024-10-18 15:11:40 +08:00
sutong 263a57f636 添加修改功能 2024-10-17 13:55:12 +08:00
sutong 2fb887a936 完善小游戏逻辑 2024-10-17 01:21:55 +08:00
sutong 411876b377 完善十子棋功能 2024-10-16 19:30:07 +08:00
sutong 7986bb1c9b 完善小游戏逻辑 2024-10-16 01:34:36 +08:00
sutong 2f8d833006 完善聊天功能 初步构建十子棋功能 2024-10-15 22:03:57 +08:00
sutong b3cf948fd2 完善聊天功能 初步构建十子棋功能 2024-10-15 21:13:17 +08:00
sutong 245875881c 新增聊天功能 2024-10-14 17:40:11 +08:00
sutong 99ed84f534 解决了无法重复打开的bug 2024-10-12 19:23:45 +08:00
sutong 88439dfb81 解决了打开文件时有黑框的bug 2024-10-12 16:00:32 +08:00
sutong 7a6f815597 支持了 html 类型工具 2024-10-12 15:44:16 +08:00
sutong e42744d681 修复了下载工具结束时,删除资源由于句柄未释放导致删除失败 2024-10-12 14:38:57 +08:00
sutong 7862b1d88e 完成了分享下载全部流程 2024-10-12 13:37:49 +08:00
sutong e6f5aaa02f 服务发现基础逻辑 2024-10-12 00:38:18 +08:00
sutong d1d7f56ce4 完善个人数据维护, 初步建立局域网链接逻辑 2024-10-11 18:46:40 +08:00
sutong 0716c82833 修正基本架构逻辑,基本实现离线功能 2024-10-11 01:03:17 +08:00
sutong 1a0a9466d8 拓展类型 自定义工具类型 2024-10-10 18:22:50 +08:00
sutong 3b69ba93ad 拓展类型 2024-09-25 17:45:38 +08:00
sutong a0e898e46c 单例导出权限 2024-09-13 20:40:18 +08:00
sutong a77308573e 表单数据持久化 2024-09-13 20:33:36 +08:00
sutong 250ffb4e20 表单数据持久化 2024-09-13 18:19:27 +08:00
51 changed files with 3147 additions and 254 deletions
+1
View File
@@ -4,3 +4,4 @@
/work_cation.iml /work_cation.iml
/work_cation_0.1.2.zip /work_cation_0.1.2.zip
/data.db /data.db
/data/
+1 -1
View File
@@ -15,7 +15,7 @@ var _ fyne.Theme = (*MyTheme)(nil)
//go:embed simkai.ttf //go:embed simkai.ttf
var arialNovaLight []byte var arialNovaLight []byte
//go:embed logo.jpg //go:embed wolf_logo.png
var logoData []byte var logoData []byte
var LogoDataSR = &fyne.StaticResource{ var LogoDataSR = &fyne.StaticResource{
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+20 -2
View File
@@ -3,17 +3,35 @@ package cfg
import ( import (
"fmt" "fmt"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"os"
) )
type Config struct { type Config struct {
DbPath string `ini:"dbPath"` DbPath string `ini:"db_path"`
CardDir string `ini:"card_dir"`
CardInfo string `ini:"card_info"`
Avatar string `ini:"avatar"`
ZeroconfKey string `ini:"zeroconf_key"`
ZeroconfPort int `ini:"zeroconf_port"`
ServerAddr string `ini:"server_addr"`
} }
var T = Config{ var T = Config{
DbPath: "./data.db", DbPath: "./data/data.db",
CardDir: "./data",
CardInfo: "package.json",
Avatar: "./data/avatar",
ZeroconfKey: "work_cations_service",
ZeroconfPort: 16800,
ServerAddr: ":16801",
} }
func init() { func init() {
defer func() {
_ = os.Mkdir(T.CardDir, os.ModeDir)
_ = os.Mkdir(T.Avatar, os.ModeDir)
}()
cfg, err := ini.Load("conf.ini") cfg, err := ini.Load("conf.ini")
if err != nil { if err != nil {
fmt.Println("读取配置错误1", err) fmt.Println("读取配置错误1", err)
+16
View File
@@ -0,0 +1,16 @@
package main
import (
"fmt"
"testing"
"work_cation/views"
)
func TestP1(t *testing.T) {
r := views.IsArithmeticSequence2([]int{0, 4, 8}, 8, 3, 3, 3)
fmt.Println(r)
}
func TestP2(t *testing.T) {
}
+7
View File
@@ -0,0 +1,7 @@
db_path=data/data.db
card_dir=data
card_info=package.json
avatar=data/avatar
zeroconf_key=work_cations_service
zeroconf_port=16800
server_addr=:16805
+4 -3
View File
@@ -4,8 +4,8 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"work_cation/cfg" "work_cation/cfg"
"work_cation/models" "work_cation/models"
"work_cation/models/repo"
"work_cation/pkg/gormx" "work_cation/pkg/gormx"
"work_cation/repo"
) )
var DB *gorm.DB var DB *gorm.DB
@@ -17,14 +17,15 @@ func InitDB() {
panic(err) panic(err)
} }
err = DB.AutoMigrate( err = DB.AutoMigrate(
&models.ErlangCards{},
&models.ErlangCardCollections{}, &models.ErlangCardCollections{},
&models.History{}, &models.History{},
&models.Users{}, &models.Users{},
&models.UserFollows{}, &models.UserFollows{},
&models.BaseCard{},
&models.ChatMessage{},
) )
if err != nil { if err != nil {
panic(err) panic(err)
} }
repo.UserRepo.GetUserInfo(DB) repo.User.GetUserInfo(DB)
} }
+197
View File
@@ -0,0 +1,197 @@
package global
import (
"bytes"
"encoding/json"
"fmt"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
"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 User desc
// First SELECT `id`,`value` FROM `test_models` WHERE id = 1 AND `test_models`.`deleted_at` IS NULL ORDER BY User desc,`test_models`.`id` LIMIT 1
// row value misused 滥用
return tx.
Where("id in ?", id).
//Or("id = ? ", id+1).
Select("value").
Order("User 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: <nil>
// Save SELECT `id`,`value` FROM `test_models` WHERE id = 1 AND `test_models`.`deleted_at` IS NULL ORDER BY User 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("User 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)
}
func TestString(t *testing.T) {
var name string = "苏通"
newName, _ := convertToUTF8(name)
fmt.Printf(name)
fmt.Printf(newName)
}
func convertToUTF8(input string) (string, error) {
// 定义其他编码到 UTF-8 的转换器
reader := transform.NewReader(bytes.NewReader([]byte(input)), simplifiedchinese.GBK.NewDecoder())
// 读取转换后的数据
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
utf8Str := buf.String()
return utf8Str, nil
}
type TestMode2 struct {
Name string `json:"name"`
Level int
}
type TestMode3 struct {
T TestMode2
Name string `json:"name1"`
}
func TestP11(t *testing.T) {
var t1 = TestMode3{
T: TestMode2{Name: "测试1", Level: 1},
Name: "测试2",
}
fmt.Println(t1)
}
+33
View File
@@ -0,0 +1,33 @@
package global
import "sync"
type RunGameInfo struct {
Uuid string
Type TyGameType
Obj any
}
var RunGames = make(map[string]*RunGameInfo)
var lock = sync.Mutex{}
func GetGameInfo(uuid string) *RunGameInfo {
return RunGames[uuid]
}
func CreateGame(game *RunGameInfo) {
lock.Lock()
defer lock.Unlock()
RunGames[game.Uuid] = game
}
func DeleteGame(uuid string) {
lock.Lock()
defer lock.Unlock()
delete(RunGames, uuid)
}
type TyGameType = int
const (
GameType1 TyGameType = iota
)
+11
View File
@@ -0,0 +1,11 @@
package global
import "work_cation/models"
type SendGlobal struct {
SendChan chan *models.Message
}
var Send = &SendGlobal{
SendChan: make(chan *models.Message, 1000),
}
+33 -5
View File
@@ -17,39 +17,67 @@ require (
require ( require (
fyne.io/systray v1.11.0 // indirect fyne.io/systray v1.11.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect github.com/BurntSushi/toml v1.4.0 // indirect
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect
github.com/bytedance/sonic v1.12.3 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fredbi/uri v1.1.0 // indirect github.com/fredbi/uri v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect
github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.10.0 // indirect
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/go-text/render v0.1.1-0.20240418202334-dd62631dae9b // indirect github.com/go-text/render v0.1.1-0.20240418202334-dd62631dae9b // indirect
github.com/go-text/typesetting v0.1.0 // indirect github.com/go-text/typesetting v0.1.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/grandcat/zeroconf v1.0.0 // indirect
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lestrrat-go/strftime v1.1.0 // indirect github.com/lestrrat-go/strftime v1.1.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/miekg/dns v1.1.27 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect
github.com/pkg/errors v0.8.1 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.2.6 // 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/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
github.com/stretchr/testify v1.8.4 // indirect github.com/stretchr/testify v1.9.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/yuin/goldmark v1.7.1 // indirect github.com/yuin/goldmark v1.7.1 // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/image v0.18.0 // indirect golang.org/x/image v0.18.0 // indirect
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.20.0 // indirect golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.19.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
+82
View File
@@ -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 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= 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/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/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/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= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -53,11 +55,22 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -88,7 +101,13 @@ github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a h1:ybgRdYvAHTn93HW
github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY= github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY=
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk= github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk=
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0= github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk= github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -96,6 +115,12 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-text/render v0.1.1-0.20240418202334-dd62631dae9b h1:daoFn+Aw8EIQZO9kYWwHL01FqwwpCl2nTeVEYbsgRHk= github.com/go-text/render v0.1.1-0.20240418202334-dd62631dae9b h1:daoFn+Aw8EIQZO9kYWwHL01FqwwpCl2nTeVEYbsgRHk=
@@ -104,6 +129,8 @@ github.com/go-text/typesetting v0.1.0 h1:vioSaLPYcHwPEPLT7gsjCGDCoYSbljxoHJzMnKw
github.com/go-text/typesetting v0.1.0/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI= github.com/go-text/typesetting v0.1.0/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI=
github.com/go-text/typesetting-utils v0.0.0-20240329101916-eee87fb235a3 h1:levTnuLLUmpavLGbJYLJA7fQnKeS7P1eCdAlM+vReXk= github.com/go-text/typesetting-utils v0.0.0-20240329101916-eee87fb235a3 h1:levTnuLLUmpavLGbJYLJA7fQnKeS7P1eCdAlM+vReXk=
github.com/go-text/typesetting-utils v0.0.0-20240329101916-eee87fb235a3/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/go-text/typesetting-utils v0.0.0-20240329101916-eee87fb235a3/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -179,6 +206,8 @@ github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNY
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY= github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -212,6 +241,8 @@ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk= github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
@@ -219,11 +250,17 @@ github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6U
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
@@ -233,9 +270,13 @@ github.com/lestrrat-go/strftime v1.1.0/go.mod h1:uzeIB52CeUJenCo1syghlugshMysrqU
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
@@ -245,8 +286,12 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
@@ -254,9 +299,13 @@ github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7g
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
@@ -287,11 +336,16 @@ 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/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 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 h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= 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= github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -299,9 +353,16 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -323,6 +384,8 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -331,6 +394,10 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -386,6 +453,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -411,6 +479,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -445,6 +515,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -480,8 +551,12 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -493,6 +568,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -518,6 +595,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -655,6 +733,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
@@ -686,6 +766,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+12 -3
View File
@@ -1,17 +1,18 @@
package main package main
import ( import (
"fmt"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/app" "fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"work_cation/assets" "work_cation/assets"
"work_cation/global" "work_cation/global"
"work_cation/service"
"work_cation/version" "work_cation/version"
"work_cation/views" "work_cation/views"
) )
// 本地缓存 上次打开目录
const preferenceCurrentTutorial = "currentTutorial" const preferenceCurrentTutorial = "currentTutorial"
func main() { func main() {
@@ -21,7 +22,7 @@ func main() {
a := app.NewWithID("io.fyne.workCation") a := app.NewWithID("io.fyne.workCation")
a.Settings().SetTheme(&assets.MyTheme{}) a.Settings().SetTheme(&assets.MyTheme{})
a.SetIcon(assets.LogoDataSR) a.SetIcon(assets.LogoDataSR)
w := a.NewWindow("主页 " + version.NowVersion) w := a.NewWindow("开发小工具 " + version.NowVersion)
w.Resize(fyne.NewSize(800, 550)) w.Resize(fyne.NewSize(800, 550))
w.SetContent(mainView(w)) w.SetContent(mainView(w))
w.CenterOnScreen() w.CenterOnScreen()
@@ -33,6 +34,13 @@ func main() {
// 主界面 // 主界面
func mainView(w fyne.Window) fyne.CanvasObject { func mainView(w fyne.Window) fyne.CanvasObject {
service.Zeroconf.StartFindService()
views.ListenChat()
// 启动服务
if err := service.Server.StartListenServer(); err != nil {
return widget.NewLabel(err.Error())
}
views.StartGameListen()
var ( var (
content = container.NewMax() content = container.NewMax()
a = fyne.CurrentApp() a = fyne.CurrentApp()
@@ -102,13 +110,14 @@ func makeNav(setTutorial func(tutorial views.Tutorial), loadPrevious bool) fyne.
if unsupportedTutorial(t) { if unsupportedTutorial(t) {
return return
} }
// 存储默认打开
a.Preferences().SetString(preferenceCurrentTutorial, uid) a.Preferences().SetString(preferenceCurrentTutorial, uid)
fmt.Println(t)
setTutorial(t) setTutorial(t)
} }
}, },
} }
// 读取存储默认打开
if loadPrevious { if loadPrevious {
currentPref := a.Preferences().StringWithFallback(preferenceCurrentTutorial, "welcome") currentPref := a.Preferences().StringWithFallback(preferenceCurrentTutorial, "welcome")
tree.Select(currentPref) tree.Select(currentPref)
+37
View File
@@ -0,0 +1,37 @@
package models
import (
"time"
"work_cation/pkg/gormx"
"work_cation/pkg/utils"
)
type BaseCard 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"` // 封面
ToolType string `json:"tool_type"` // 工具类型
UpdateTx time.Time `json:"update_tx"` // 更新时间
Number int `json:"number"` // 数量
Goods int `json:"goods"` // 点赞数
Collection int `json:"collection"` // 收藏数
}
const (
ToolTypeErlang = "erlangCard" // 卡片
ToolTypeNoVarErlang = "erlangNoVarCard"
ToolTypeExecFiles = "execFiles" // 可执行文件
)
func NewBaseCard(t, userId string) BaseCard {
return BaseCard{
UUID: utils.Uuid.CreateUUID(),
UserID: userId,
Title: "未命名",
Text: "未命名",
ToolType: t,
UpdateTx: time.Now(),
}
}
+1 -9
View File
@@ -3,17 +3,9 @@ package models
import "work_cation/pkg/gormx" import "work_cation/pkg/gormx"
type ErlangCards struct { type ErlangCards struct {
UUID string `json:"uuid"` // id ip加时间戳生成 BaseCard
UserIp string `json:"user_ip"` // 用户ip
Title string `json:"title"` // 标题
Text string `json:"text"` // 内容
Covers gormx.ListString `json:"covers"` // 封面
Template string `json:"template"` // 模版 Template string `json:"template"` // 模版
VarName gormx.ListString `json:"var_name"` // 变量 VarName gormx.ListString `json:"var_name"` // 变量
VarContent gormx.ListString `json:"var_content"` // 变量内容 VarContent gormx.ListString `json:"var_content"` // 变量内容
IsShowOut bool `json:"is_show_out"` // 是否展示 IsShowOut bool `json:"is_show_out"` // 是否展示
Number int `json:"number"` // 数量
Goods int `json:"goods"` // 点赞
Collection int `json:"collection"` // 收藏
} }
+9
View File
@@ -0,0 +1,9 @@
package models
type ExecFiles struct {
BaseCard
// 文件名
Cmd string `json:"common"` // 执行命令
Pwd string `json:"pwd"` // 执行路径
Files []string `json:"files"` // 原始文件
}
+9
View File
@@ -0,0 +1,9 @@
package models
type GameMessage struct {
Router string `json:"router"`
User *Users `json:"user_id"`
Pos int `json:"pos"`
GameType int
GameUuid string
}
+55
View File
@@ -0,0 +1,55 @@
package models
import (
"time"
)
type ChatMessage struct {
UserID string // 发言对象
UserName string // 发言对象名字
TarUserID string // 接收对象
CreateTx time.Time
Type string
Text string
}
const (
ChatTypeText = "TEXT" // 对话
ChatTypeCard = "CARD" // 工具卡片分享
)
func NewTextChatMsg(user *Users, tarUid string, text string) *ChatMessage {
return &ChatMessage{
UserID: user.ID,
UserName: user.Name,
TarUserID: tarUid,
CreateTx: time.Now(),
Type: ChatTypeText,
Text: text,
}
}
type Message struct {
Cmd string
User UserFollows
ChatMessage ChatMessage
}
//
//// Value 接口,Value 返回 json value any -> string
//func (j *Users) Value() (driver.Value, error) {
// return json.Marshal(j)
//}
//
//// Scan 接口,Scan 将 value 扫描至 Jsonb
//func (j *Users) Scan(value interface{}) error {
// bytes, ok := value.([]byte)
// if !ok {
// return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
// }
// err := json.Unmarshal(bytes, j)
// if err != nil {
// return err
// }
// return nil
//}
+47
View File
@@ -0,0 +1,47 @@
package models
import (
"errors"
"fmt"
"github.com/grandcat/zeroconf"
"net"
"work_cation/cfg"
)
// Online 在线数据
type Online struct {
AddrIPv4 []net.IP `json:"-"` // Host machine IPv4 address
AddrIPv6 []net.IP `json:"-"`
Port string `json:"port"`
ID string `json:"id"`
Ip string `json:"ip"`
}
func (o *Online) Url(router string) string {
if o.Port == "" {
o.Port = cfg.T.ServerAddr
}
return fmt.Sprintf("http://%s%s%s", o.Ip, o.Port, router)
}
func UserTOnlineList(user *Users) []string {
return []string{
user.ID,
user.Ip,
cfg.T.ServerAddr,
}
}
func NewOnline(entry *zeroconf.ServiceEntry) (*Online, error) {
if len(entry.Text) != 3 {
return nil, errors.New("invalid online entry")
}
return &Online{
AddrIPv4: entry.AddrIPv4,
AddrIPv6: entry.AddrIPv6,
Port: entry.Text[2],
ID: entry.Text[0],
Ip: entry.Text[1],
}, nil
}
-20
View File
@@ -1,20 +0,0 @@
package repo
import (
"gorm.io/gorm"
"work_cation/models"
)
type ErlangCard struct{}
var ErlangCardRepo *ErlangCard
func (*ErlangCard) FindAll(db *gorm.DB) []models.ErlangCards {
var cards []models.ErlangCards
db.Find(&cards)
return cards
}
func (*ErlangCard) Create(db *gorm.DB, erlangCard *models.ErlangCards) error {
return db.Create(erlangCard).Error
}
-35
View File
@@ -1,35 +0,0 @@
package repo
import (
"gorm.io/gorm"
"os/user"
"work_cation/models"
"work_cation/pkg/utils"
)
type User struct{}
var UserRepo *User
func (*User) GetUserInfo(db *gorm.DB) *models.Users {
var users models.Users
db.Find(&users)
ip := utils.Get192Ip()
if users.Ip != ip {
current, err := user.Current()
if err != nil {
current = &user.User{Name: ip}
}
users = models.Users{
Ip: ip,
Name: current.Username,
Avatar: "",
Cover: "",
Email: "",
Phone: "",
Address: "友嘉",
}
db.Create(&users)
}
return &users
}
+12 -7
View File
@@ -1,11 +1,16 @@
package models package models
import "time"
type Users struct { type Users struct {
Ip string `gorm:"primarykey" json:"ip"` ID string `gorm:"primarykey" json:"id"`
Name string `json:"name"` // 昵称 Ip string `json:"ip"`
Avatar string `json:"avatar"` // 头像 UpdateTx time.Time `json:"update_tx"`
Cover string `json:"cover"` // 封面 Port string `json:"port"`
Email string `json:"email"` Name string `json:"name"` // 昵称
Phone string `json:"phone"` Avatar string `json:"avatar"` // 头像
Address string `json:"address"` Cover string `json:"cover"` // 封面
Email string `json:"email"`
Phone string `json:"phone"`
Address string `json:"address"` // 工位
} }
+1
View File
@@ -3,4 +3,5 @@ package models
// UserFollows 关注用户 // UserFollows 关注用户
type UserFollows struct { type UserFollows struct {
Users Users
Sort int
} }
+51
View File
@@ -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
}
+101
View File
@@ -0,0 +1,101 @@
package utils
import (
"fmt"
"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
}
func (*FileUtils) WinOpenFile(dir string, fileName string) error {
_, err := Exec.Command(fmt.Sprintf("cmd /c start %s", fileName), dir)
return err
}
+13
View File
@@ -0,0 +1,13 @@
package utils
import (
"fmt"
"syscall"
"testing"
)
func TestP1(t *testing.T) {
text := "test"
data := syscall.StringToUTF16(text)
fmt.Println(data)
}
+25 -7
View File
@@ -9,19 +9,37 @@ import (
"time" "time"
) )
var uuidint = 0 type uuidUtil struct {
var lock sync.Mutex lock sync.Mutex
uuidint int
}
func CreateUUID() string { var Uuid = &uuidUtil{}
lock.Lock()
defer lock.Unlock() func (u *uuidUtil) CreateUUID() string {
u.lock.Lock()
defer u.lock.Unlock()
// 生成基于字符串的 UUID // 生成基于字符串的 UUID
key := fmt.Sprintln(Get192Ip(), time.Now().Format("2006-01-02_15-04-05"), uuidint) key := fmt.Sprintln(IP.Get192Ip(), time.Now().Format("2006-01-02_15-04-05"), u.uuidint)
uuidint++ u.uuidint++
u1 := uuid.NewSHA1(uuid.Nil, []byte(key)) u1 := uuid.NewSHA1(uuid.Nil, []byte(key))
return u1.String() return u1.String()
} }
type ipUtil struct {
Ip string
}
var IP = &ipUtil{}
func (i *ipUtil) Get192Ip() string {
if i.Ip == "" {
i.Ip = Get192Ip()
return i.Ip
}
return i.Ip
}
func Get192Ip() string { func Get192Ip() string {
interfaces, err := net.Interfaces() interfaces, err := net.Interfaces()
if err != nil { if err != nil {
+33
View File
@@ -0,0 +1,33 @@
package repo
import (
"gorm.io/gorm"
"work_cation/models"
)
type baseCardRepo struct{}
var BaseCard *baseCardRepo
func (*baseCardRepo) FindAll(db *gorm.DB) []models.BaseCard {
var cards []models.BaseCard
db.Find(&cards)
return cards
}
func (*baseCardRepo) Find(db *gorm.DB, uuid string) models.BaseCard {
var card models.BaseCard
db.Where("uuid = ?", uuid).Find(&card)
return card
}
func (*baseCardRepo) CreateOrSave(db *gorm.DB, baseCard *models.BaseCard) error {
var card models.BaseCard
if err := db.Where("uuid = ?", baseCard.UUID).Find(&card).Error; err != nil {
return err
}
if card.UUID != "" {
return db.Where("uuid = ?", baseCard.UUID).Save(baseCard).Error
}
return db.Create(baseCard).Error
}
+23
View File
@@ -0,0 +1,23 @@
package repo
import (
"gorm.io/gorm"
"work_cation/models"
)
type chatMessageRepo struct{}
var ChatMsg = &chatMessageRepo{}
func (*chatMessageRepo) GetUserMsgs(db *gorm.DB, uuid string, limit int) []models.ChatMessage {
var msgs []models.ChatMessage
// ASC:升序(默认),DESC:降序。
db.Order("create_tx ASC").Limit(limit).
Where("user_id = ? OR tar_user_id = ?", uuid, uuid).
Find(&msgs)
return msgs
}
func (*chatMessageRepo) Create(db *gorm.DB, msg *models.ChatMessage) error {
return db.Create(msg).Error
}
+26
View File
@@ -0,0 +1,26 @@
package repo
import (
"gorm.io/gorm"
"work_cation/models"
)
type erlangCard struct{}
var ErlangCardRepo *erlangCard
func (*erlangCard) FindAll(db *gorm.DB) []models.ErlangCards {
var cards []models.ErlangCards
db.Find(&cards)
return cards
}
func (*erlangCard) Find(db *gorm.DB, uuid string) models.ErlangCards {
var card models.ErlangCards
db.Where("uuid = ?", uuid).First(&card)
return card
}
func (*erlangCard) Create(db *gorm.DB, erlangCard *models.ErlangCards) error {
return db.Create(erlangCard).Error
}
+58
View File
@@ -0,0 +1,58 @@
package repo
import (
"gorm.io/gorm"
"os/user"
"time"
"work_cation/cfg"
"work_cation/models"
"work_cation/pkg/utils"
)
type userRepo struct {
isNew bool
users models.Users
}
var User = &userRepo{}
func (u *userRepo) GetUserInfo(db *gorm.DB) *models.Users {
if u.isNew {
return &u.users
}
var users models.Users
ip := utils.IP.Get192Ip()
db.Where("ip = ?", ip).Find(&users)
if users.Ip != ip {
current, err := user.Current()
if err != nil {
current = &user.User{Name: ip}
}
users = models.Users{
ID: utils.Uuid.CreateUUID(),
Ip: ip,
Name: current.Username,
Cover: "",
Email: "",
Phone: "",
Address: "友嘉",
}
db.Create(&users)
}
if users.Port != cfg.T.ServerAddr {
users.Port = cfg.T.ServerAddr
users.UpdateTx = time.Now()
db.Model(&models.Users{}).Where("id = ?", users.ID).
Updates(users)
}
u.isNew = true
u.users = users
return &users
}
func (u *userRepo) Update(db *gorm.DB, newUser *models.Users) error {
u.isNew = false
return db.Model(&models.Users{}).Where("id = ?", newUser.ID).
Updates(newUser).Error
}
+34
View File
@@ -0,0 +1,34 @@
package repo
import (
"gorm.io/gorm"
"work_cation/models"
)
type userFollowRepo struct{}
var UserFollow = &userFollowRepo{}
func (u *userFollowRepo) Follow(db *gorm.DB, user *models.UserFollows) error {
return db.Model(&models.UserFollows{}).Create(user).Error
}
func (u *userFollowRepo) GetUser(db *gorm.DB, uuid string) *models.UserFollows {
var user models.UserFollows
err := db.Model(&models.UserFollows{}).Where("id = ?", uuid).Find(&user).Error
if err != nil {
return nil
}
if user.ID == "" {
return nil
}
return &user
}
func (u *userFollowRepo) UnFollow(db *gorm.DB, user *models.UserFollows) error {
return db.Model(&models.UserFollows{}).Where("id = ?", user.ID).Delete(user).Error
}
func (u *userFollowRepo) All(db *gorm.DB) ([]models.UserFollows, error) {
var users []models.UserFollows
err := db.Model(&models.UserFollows{}).Find(&users).Error
return users, err
}
+60
View File
@@ -0,0 +1,60 @@
package service
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"gorm.io/gorm"
"os"
"path/filepath"
"work_cation/cfg"
"work_cation/global"
"work_cation/models"
"work_cation/repo"
)
type BaseCardService struct{}
var BaseCard = &BaseCardService{}
func (*BaseCardService) Create(db *gorm.DB, baseCard models.BaseCard) {
repo.BaseCard.CreateOrSave(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
}
func (*BaseCardService) DownloadCard(w fyne.Window, baseCardCopy models.BaseCard, online *models.Online) {
progress := binding.NewFloat()
progress.Set(0)
progressBar := widget.NewProgressBarWithData(progress)
progressBar.Max = 120
smaillWin := dialog.NewCustom("下载中", "关闭", progressBar, w)
smaillWin.Show()
err := Client.Download(online, baseCardCopy.UUID, progress)
if err != nil {
smaillWin.SetDismissText("失败")
return
}
// 修改数据
progress.Set(110)
err = repo.BaseCard.CreateOrSave(global.DB, &baseCardCopy)
if err != nil {
smaillWin.SetDismissText("失败")
return
}
progress.Set(120)
smaillWin.SetDismissText("完成 请去[我的]查看")
}
+249
View File
@@ -0,0 +1,249 @@
package service
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"fmt"
"fyne.io/fyne/v2/data/binding"
"github.com/gin-gonic/gin"
"io"
"net/http"
"os"
"path/filepath"
"time"
"work_cation/cfg"
"work_cation/global"
"work_cation/models"
"work_cation/repo"
)
type ClientService struct {
}
var Client = &ClientService{}
func (c *ClientService) GetUser(online *models.Online) (*models.Users, error) {
var user models.Users
ctx, carnal := context.WithTimeout(context.TODO(), time.Millisecond*500)
defer carnal()
res, err := http.NewRequestWithContext(ctx, "", online.Url("/user"), nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(res)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(data, &user)
if err != nil {
return nil, err
}
return &user, nil
}
func (c *ClientService) GetCards(online *models.Online) ([]models.BaseCard, error) {
var cards []models.BaseCard
ctx, carnal := context.WithTimeout(context.TODO(), time.Millisecond*500)
defer carnal()
res, err := http.NewRequestWithContext(ctx, "", online.Url("/cards"), nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(res)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = json.Unmarshal(data, &cards)
if err != nil {
return nil, err
}
return cards, nil
}
func (c *ClientService) Download(online *models.Online, uuid string, progress binding.Float) error {
resp, err := http.Get(online.Url("/download/card/" + uuid))
if err != nil {
return err
}
defer resp.Body.Close()
downloadFile := filepath.Join(cfg.T.CardDir, fmt.Sprintf("%s.zip", uuid))
_, err = os.Stat(downloadFile)
if err == nil {
os.RemoveAll(downloadFile)
}
file, err := os.Create(downloadFile)
if err != nil {
return err
}
r := Rr{
file: file,
tot: resp.ContentLength,
progress: 0,
oneUp: 0,
progressBind: progress,
}
_, err = io.Copy(&r, resp.Body)
file.Close()
if err != nil {
return err
}
// 解压
destDir := filepath.Join(cfg.T.CardDir, uuid)
_ = os.RemoveAll(destDir)
if err = unzipFile(downloadFile, destDir); err != nil {
return err
}
_ = os.RemoveAll(downloadFile)
return nil
}
func (c *ClientService) Chat(online *models.Online, text string) (string, error) {
req := gin.H{
"text": text,
}
msg, err := PostSend("/chat", online, req)
if err != nil {
return "", err
}
return msg["message"].(string), err
}
func (c *ClientService) StartGame(online *models.Online, gameUuid string, gameType int) (string, error) {
req := gin.H{
"type": gameType,
"game_uuid": gameUuid,
}
msg, err := PostSend("/start_game", online, req)
if err != nil {
return "", err
}
return msg["message"].(string), err
}
func (c *ClientService) PlayGame(online *models.Online, gameUuid string, pos int) (string, error) {
req := gin.H{
"pos": pos,
"game_uuid": gameUuid,
}
msg, err := PostSend("/play_game", online, req)
if err != nil {
return "", err
}
return msg["message"].(string), err
}
func (c *ClientService) CloseGame(online *models.Online, gameUuid string) (string, error) {
req := gin.H{
"game_uuid": gameUuid,
}
msg, err := PostSend("/close_game", online, req)
return msg["message"].(string), err
}
type Rr struct {
file *os.File
tot int64
progress int64
oneUp int
progressBind binding.Float
}
func (r *Rr) Write(p []byte) (n int, err error) {
n, err = r.file.Write(p)
if err != nil {
return
}
r.progress += int64(n)
nowUp := int(r.progress * 100 / r.tot)
if nowUp != r.oneUp {
r.progressBind.Set(float64(nowUp))
r.oneUp = nowUp
}
return
}
func unzipFile(zipFile, destDir string) error {
// 打开ZIP文件
r, err := zip.OpenReader(zipFile)
if err != nil {
return err
}
defer r.Close()
// 创建目标目录
err = os.MkdirAll(destDir, os.ModePerm)
if err != nil {
return err
}
// 遍历ZIP文件中的文件并解压缩到目标目录
for _, f := range r.File {
rc, err := f.Open()
if err != nil {
return err
}
// 创建文件
path := filepath.Join(destDir, f.Name)
if f.FileInfo().IsDir() {
os.MkdirAll(path, os.ModePerm)
} else {
// 创建文件
file, err := os.Create(path)
if err != nil {
return err
}
_, err = io.Copy(file, rc)
if err != nil {
return err
}
file.Close()
}
rc.Close()
}
return nil
}
func PostSend(router string, online *models.Online, req gin.H) (gin.H, error) {
var user = repo.User.GetUserInfo(global.DB)
ctx, carnal := context.WithTimeout(context.TODO(), time.Millisecond*500)
defer carnal()
b, err := json.Marshal(req)
if err != nil {
return nil, err
}
res, err := http.NewRequestWithContext(ctx, "POST", online.Url(router), bytes.NewBuffer(b))
if err != nil {
return nil, err
}
res.Header.Set("Content-Type", "application/json")
res.Header.Set("User-User", user.ID)
resp, err := http.DefaultClient.Do(res)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
var msg = make(map[string]any)
if err = json.Unmarshal(data, &msg); err != nil {
return nil, err
}
return msg, err
}
+59
View File
@@ -0,0 +1,59 @@
package service
import (
"encoding/json"
"gorm.io/gorm"
"os"
"path/filepath"
"work_cation/cfg"
"work_cation/global"
"work_cation/models"
"work_cation/repo"
)
type erlangCardService struct{}
var ErlangCard = &erlangCardService{}
func (*erlangCardService) Create(erlangCard *models.ErlangCards) error {
if len(erlangCard.VarName) == 0 {
erlangCard.ToolType = models.ToolTypeNoVarErlang
}
os.RemoveAll(filepath.Join(cfg.T.CardDir, erlangCard.UUID))
err := os.Mkdir(filepath.Join(cfg.T.CardDir, erlangCard.UUID), os.ModeDir)
if err != nil {
return err
}
info, err := os.Create(filepath.Join(cfg.T.CardDir, erlangCard.UUID, cfg.T.CardInfo))
if err != nil {
return err
}
defer info.Close()
jsonB, err := json.Marshal(erlangCard)
if err != nil {
return err
}
_, err = info.Write(jsonB)
if err != nil {
return err
}
return global.DB.Transaction(func(tx *gorm.DB) error {
var baseCard = erlangCard.BaseCard
err = repo.BaseCard.CreateOrSave(global.DB, &baseCard)
return err
})
}
func (*erlangCardService) GetInfoPath(erlangCard *models.ErlangCards) string {
return filepath.Join(cfg.T.CardDir, erlangCard.UUID, cfg.T.CardInfo)
}
func (*erlangCardService) GetImageDir(erlangCard *models.ErlangCards) []string {
var coverPaths []string
for _, cover := range erlangCard.Covers {
coverPaths = append(coverPaths, filepath.Join(cfg.T.CardDir, erlangCard.UUID, cover))
}
return coverPaths
}
+54
View File
@@ -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.CreateOrSave(tx, &baseCard)
})
}
+180
View File
@@ -0,0 +1,180 @@
package service
import (
"archive/zip"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"io"
"log"
"net/http"
"os"
"path/filepath"
"work_cation/cfg"
"work_cation/global"
"work_cation/models"
"work_cation/repo"
)
type ServerService struct {
Status string
server *http.Server
Router *gin.Engine
}
const (
StatusOnline = "online"
StatusOffline = "offline"
)
var Server = &ServerService{Status: StatusOffline}
func (s *ServerService) Online() error {
if s.Status == StatusOnline {
return nil
}
// 启动服务发现
user := repo.User.GetUserInfo(global.DB)
if err := Zeroconf.Register(user); err != nil {
return err
}
// 修改状态
s.Status = StatusOnline
return nil
}
func (s *ServerService) StatusOffline() error {
//if err := s.server.Close(); err != nil {
// return err
//}
// 关闭服务发现
Zeroconf.Close()
// 修改状态
s.Status = StatusOffline
return nil
}
func (s *ServerService) StartListenServer() error {
router := gin.Default()
s.Router = router
router.GET("/user", func(c *gin.Context) {
user := repo.User.GetUserInfo(global.DB)
c.JSON(200, user)
})
router.GET("/cards", func(c *gin.Context) {
cards := repo.BaseCard.FindAll(global.DB)
c.JSON(200, cards)
})
router.GET("/download/card/:uuid", func(c *gin.Context) {
uuid := c.Param("uuid")
card := repo.BaseCard.Find(global.DB, uuid)
if card.UUID == "" {
c.JSON(404, nil)
return
}
cardPath := filepath.Join(cfg.T.CardDir, card.UUID)
// 设置响应头
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s.zip", uuid))
c.Header("Content-Type", "application/zip")
err := zipFolder(cardPath, c.Writer)
if err != nil {
c.JSON(500, err)
return
}
})
router.POST("/chat", func(c *gin.Context) {
user, msg, is := PublicPostCheck(c)
if !is {
return
}
message := &models.Message{
Cmd: "chat",
User: *user,
ChatMessage: *models.NewTextChatMsg(
&user.Users,
repo.User.GetUserInfo(global.DB).ID,
msg["text"].(string)),
}
global.Send.SendChan <- message
c.JSON(200, gin.H{"message": "ok"})
})
srv := &http.Server{
Addr: cfg.T.ServerAddr,
Handler: router,
}
go func() {
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listen: %s\n", err)
}
}()
s.server = srv
return nil
}
func zipFolder(folderPath string, zipFile io.Writer) error {
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
err := filepath.Walk(folderPath, func(filePath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name, err = filepath.Rel(folderPath, filePath)
if err != nil {
return err
}
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}
if !info.IsDir() {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
return nil
}
func PublicPostCheck(c *gin.Context) (*models.UserFollows, map[string]interface{}, bool) {
uuid := c.GetHeader("User-User")
user := repo.UserFollow.GetUser(global.DB, uuid)
if user.Ip != c.ClientIP() {
c.JSON(200, gin.H{"message": "对方未关注你"})
return nil, nil, false
}
var msg = make(map[string]interface{})
if err := c.ShouldBind(&msg); err != nil {
c.JSON(200, gin.H{"message": "输入异常"})
return nil, nil, false
}
return user, msg, true
}
+77
View File
@@ -0,0 +1,77 @@
package service
import (
"context"
"fmt"
"github.com/grandcat/zeroconf"
"slices"
"work_cation/cfg"
"work_cation/models"
)
type zeroconfService struct {
server *zeroconf.Server
onlines []models.Online
}
var Zeroconf = &zeroconfService{}
// Register 注册被发现服务
func (s *zeroconfService) Register(user *models.Users) error {
if s.server != nil {
return nil // already registered
}
server, err := zeroconf.Register(cfg.T.ZeroconfKey, "_http._tcp",
"local.", cfg.T.ZeroconfPort, models.UserTOnlineList(user), nil)
if err != nil {
return err
}
s.server = server
return nil
}
func (s *zeroconfService) Close() {
if s.server != nil {
s.server.Shutdown()
s.server = nil
}
}
// FindService 查找被发现服务
func (s *zeroconfService) FindService() ([]models.Online, error) {
return s.onlines, nil
}
func (s *zeroconfService) GetOnline(id string) *models.Online {
for _, online := range s.onlines {
if online.ID == id {
return &online
}
}
return nil
}
func (s *zeroconfService) StartFindService() error {
resolver, err := zeroconf.NewResolver(nil)
if err != nil {
return err
}
allEntries := make(chan *zeroconf.ServiceEntry)
go func(results <-chan *zeroconf.ServiceEntry) {
for entry := range results {
fmt.Println(entry.AddrIPv4, entry.ServiceInstanceName())
if entry.ServiceInstanceName() == fmt.Sprintf("%s._http._tcp.local.", cfg.T.ZeroconfKey) {
online, err := models.NewOnline(entry)
if err != nil {
continue
}
delOnlines := slices.DeleteFunc(s.onlines, func(item models.Online) bool {
return item.ID == online.ID
})
s.onlines = append(delOnlines, *online)
}
}
}(allEntries)
err = resolver.Browse(context.Background(), "_http._tcp", "local.", allEntries)
return err
}
+174
View File
@@ -0,0 +1,174 @@
package views
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/gin-gonic/gin"
"time"
"work_cation/global"
"work_cation/models"
"work_cation/repo"
"work_cation/service"
)
type ChatView struct {
usersChat map[string]*ChatUserInfo
}
type ChatUserInfo struct {
models.Users
messages []models.ChatMessage
shows *fyne.Container
scroll *container.Scroll
w fyne.Window
}
var chat = &ChatView{usersChat: make(map[string]*ChatUserInfo)}
func ListenChat() {
go func() {
for {
message := <-global.Send.SendChan
userInfo := chat.usersChat[message.User.ID]
if userInfo == nil {
userInfo = &ChatUserInfo{}
chat.usersChat[message.User.ID] = userInfo
userInfo.messages = repo.ChatMsg.GetUserMsgs(global.DB, message.User.ID, -1)
}
userInfo.messages = append(userInfo.messages, message.ChatMessage)
_ = repo.ChatMsg.Create(global.DB, &message.ChatMessage)
userInfo.Users = message.User.Users
if userInfo.shows == nil {
OpenChat(message.User.Users)
} else {
userInfo.shows.Add(itemMessage(message.ChatMessage))
userInfo.w.RequestFocus()
userInfo.scroll.ScrollToBottom()
}
}
}()
}
func OpenChat(user models.Users) {
userInfo := chat.usersChat[user.ID]
if userInfo == nil {
userInfo = &ChatUserInfo{}
chat.usersChat[user.ID] = userInfo
userInfo.messages = repo.ChatMsg.GetUserMsgs(global.DB, user.ID, -1)
}
if userInfo.shows == nil {
w := fyne.CurrentApp().NewWindow(fmt.Sprintf("%s %s", user.Name, user.Ip))
w.CenterOnScreen()
w.Resize(fyne.NewSize(500, 420))
list := container.NewVBox()
for _, item := range userInfo.messages {
list.Add(itemMessage(item))
}
scroll := container.NewScroll(list)
userInfo.scroll = scroll
scroll.ScrollToBottom()
// 发送表单
en := widget.NewEntry()
submit := func() {
if en.Text == "" {
return
}
online := service.Zeroconf.GetOnline(user.ID)
if online == nil {
online = &models.Online{ID: user.ID, Ip: user.Ip, Port: user.Port}
}
msg, err := service.Client.Chat(online, en.Text)
if err != nil || msg != "ok" {
dialog.ShowInformation("发送失败", err.Error(), w)
return
}
my := repo.User.GetUserInfo(global.DB)
chatItem := models.NewTextChatMsg(my, user.ID, en.Text)
userInfo.messages = append(userInfo.messages, *chatItem)
_ = repo.ChatMsg.Create(global.DB, chatItem)
list.Add(itemMessage(*chatItem))
en.SetText("")
scroll.ScrollToBottom()
}
online := &models.Online{ID: user.ID, Ip: user.Ip, Port: user.Port}
// 分享
toolBar := widget.NewToolbar(
widget.NewToolbarAction(theme.ContentAddIcon(), func() {
dialog.ShowInformation("未开发", "分享脚本功能 尽请期待", w)
}),
widget.NewToolbarAction(theme.CancelIcon(), func() { w.Close() }),
widget.NewToolbarAction(theme.WarningIcon(), func() {
var ilo *dialog.CustomDialog
box := container.NewVBox()
for _, item := range GameConfigList {
var itemCopy = item
box.Add(widget.NewButton(itemCopy.Name, func() {
req := gin.H{
"name": itemCopy.Name,
"x": itemCopy.X,
"y": itemCopy.Y,
"win": itemCopy.Win}
_, err := service.PostSend("/send_game", online, req)
if err != nil {
dialog.ShowError(err, w)
}
ilo.Hide()
}))
}
ilo = dialog.NewCustomWithoutButtons("选择", box, w)
ilo.Show()
}),
)
button := widget.NewButton("", submit)
button.SetIcon(theme.ConfirmIcon())
en.OnSubmitted = func(_ string) { submit() }
w.SetContent(container.NewBorder(nil,
container.NewBorder(nil, toolBar, nil, button, en),
nil, nil, scroll))
userInfo.shows = list
userInfo.w = w
w.SetOnClosed(func() {
userInfo.shows = nil
userInfo.scroll = nil
userInfo.w = nil
})
w.Show()
}
userInfo.w.RequestFocus()
userInfo.scroll.ScrollToBottom()
}
func itemMessage(msg models.ChatMessage) fyne.CanvasObject {
my := repo.User.GetUserInfo(global.DB)
var card *widget.Card
if msg.UserID != my.ID {
title := fmt.Sprintf("%s %s", msg.CreateTx.Format(time.DateTime), msg.UserName)
card = widget.NewCard("", "",
container.NewVBox(
widget.NewLabel(title),
newLabel(msg.Text, fyne.TextAlignLeading)))
} else {
title := fmt.Sprintf("%s %s", msg.CreateTx.Format(time.DateTime), "我")
card = widget.NewCard("", "",
container.NewVBox(
widget.NewLabelWithStyle(title, fyne.TextAlignTrailing, fyne.TextStyle{}),
newLabel(msg.Text, fyne.TextAlignTrailing)))
}
return card
}
func newLabel(title string, ali fyne.TextAlign) *widget.Label {
text := widget.NewLabelWithStyle(title, ali, fyne.TextStyle{})
text.Wrapping = fyne.TextWrapBreak
return text
}
+31
View File
@@ -0,0 +1,31 @@
package views
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"sort"
)
// allCreateCards 创建卡片列表
func allCreateCards(_ fyne.Window) fyne.CanvasObject {
var itemList []fyne.CanvasObject
keys := make([]string, 0, len(cardTypeMap))
for key := range cardTypeMap {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
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...))
}
-101
View File
@@ -1,101 +0,0 @@
package views
import (
"errors"
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"regexp"
"work_cation/global"
"work_cation/models"
"work_cation/models/repo"
"work_cation/pkg/utils"
)
func CreateErlangCard(w fyne.Window) fyne.CanvasObject {
// user := repo.UserRepo.GetUserInfo(global.DB)
var erlangCard = models.ErlangCards{
UUID: utils.CreateUUID(),
UserIp: utils.Get192Ip(),
}
var items = []*widget.FormItem{
{Text: "名称", Widget: widget.NewEntry()},
{Text: "描述", Widget: widget.NewEntry()},
{Text: "封面", Widget: widget.NewEntry(), HintText: "暂未支持可不填"},
{Text: "是否默认展示", Widget: widget.NewEntry()},
{Text: "模版内容", Widget: widget.NewEntry()},
}
var items2 []*widget.FormItem
form2 := &widget.Form{
Items: items2,
SubmitText: "保存创建",
OnSubmit: func() {
for _, item := range items {
switch item.Text {
case "名称":
erlangCard.Title = item.Widget.(*widget.Entry).Text
case "描述":
erlangCard.Text = item.Widget.(*widget.Entry).Text
case "封面":
erlangCard.Covers = []string{item.Widget.(*widget.Entry).Text}
case "是否默认展示":
erlangCard.IsShowOut = item.Widget.(*widget.Entry).Text == "1"
case "模版内容":
erlangCard.Template = item.Widget.(*widget.Entry).Text
default:
}
}
if countVarOccurrences(erlangCard.Template) != len(items2) {
dialog.ShowError(errors.New("变量名数量异常"), w)
return
}
for _, item2 := range items2 {
fmt.Println("key:", item2.Text, "value:", item2.Widget.(*widget.Entry).Text)
erlangCard.VarName = append(erlangCard.VarName, item2.Widget.(*widget.Entry).Text)
}
err := repo.ErlangCardRepo.Create(global.DB, &erlangCard)
if err != nil {
dialog.ShowError(err, w)
}
},
}
form := &widget.Form{
Items: items,
SubmitText: "生成变量表单",
OnSubmit: func() {
for _, item := range items {
switch item.Text {
case "模版内容":
varSum := countVarOccurrences(item.Widget.(*widget.Entry).Text)
for i := 0; i < varSum; i++ {
newItem := &widget.FormItem{Text: fmt.Sprintf("变量%d", i+1), Widget: widget.NewEntry()}
items2 = append(items2, newItem)
form2.AppendItem(newItem)
}
default:
}
}
},
}
return container.NewBorder(widget.NewSeparator(), nil, nil, nil, container.NewScroll(
container.NewVBox(
form,
form2,
)))
}
func countVarOccurrences(input string) int {
re := regexp.MustCompile(`\$var`)
matches := re.FindAllStringIndex(input, -1)
return len(matches)
}
+158
View File
@@ -0,0 +1,158 @@
package createView
import (
"encoding/json"
"fmt"
"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"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"work_cation/cfg"
"work_cation/global"
"work_cation/models"
"work_cation/repo"
"work_cation/service"
)
func UpdateErlangCard(w fyne.Window, baseInfo *models.BaseCard) fyne.CanvasObject {
// 读取类型文件数据
infoPath := filepath.Join(cfg.T.CardDir, baseInfo.UUID, cfg.T.CardInfo)
bytes, err := os.ReadFile(infoPath)
if err != nil {
return widget.NewLabel(err.Error())
}
var data models.ErlangCards
err = json.Unmarshal(bytes, &data)
if err != nil {
return widget.NewLabel(err.Error())
}
return createUpdateErlangCard(w, &data)
}
func CreateErlangCard(w fyne.Window) fyne.CanvasObject {
return createUpdateErlangCard(w, nil)
}
func createUpdateErlangCard(w fyne.Window, erlangCard *models.ErlangCards) fyne.CanvasObject {
if erlangCard == nil {
// 创建数据
erlangCard = &models.ErlangCards{BaseCard: models.NewBaseCard(models.ToolTypeErlang, repo.User.GetUserInfo(global.DB).ID)}
}
var (
// 基础表单
formBase = &widget.Form{}
// 变量表单
formVars = &widget.Form{}
)
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) })})
// 刷新变量表单
generateVariableForm1(erlangCard, formVars)
// 保存按钮
buttonSave := widget.NewButton("保存", func() {
err := service.ErlangCard.Create(erlangCard)
if err != nil {
dialog.ShowError(err, w)
return
}
dialog.ShowInformation("ok", "创建成功", w)
time.AfterFunc(2*time.Second, func() { w.Close() })
})
return container.NewBorder(container.NewHBox(widget.NewLabel("新建"), widget.NewSeparator()),
nil, nil, nil, container.NewScroll(
container.NewVBox(
formBase,
formVars,
buttonSave,
)))
}
// 刷新表单
func generateVariableForm1(erlCard *models.ErlangCards, form2 *widget.Form) {
varSum := countVarOccurrences(erlCard.Template)
form2.Items = nil
var newVarList []string
for i := 0; i < varSum; i++ {
var item string
if i < len(erlCard.VarName) { // 如果有旧数据,则使用旧数据
item = erlCard.VarName[i]
}
var index = i
newItem := &widget.FormItem{Text: fmt.Sprintf("变量%d", i+1), Widget: newDefaultEntryCallback(&item, func() {
erlCard.VarName[index] = item
})}
form2.AppendItem(newItem)
newVarList = append(newVarList, item)
}
erlCard.VarName = newVarList
form2.Refresh()
}
func countVarOccurrences(input string) int {
re := regexp.MustCompile(`\$var`)
matches := re.FindAllStringIndex(input, -1)
return len(matches)
}
func newDefaultEntry(value any) *widget.Entry {
en := widget.NewEntry()
switch v := value.(type) {
case string:
en.SetText(v)
case bool:
if v {
en.SetText("1")
} else {
en.SetText("0")
}
case []string:
en.SetText(strings.Join(v, ","))
case *string:
data := binding.BindString(v)
en.Bind(data)
case *bool:
data := binding.NewString()
data.AddListener(binding.NewDataListener(func() {
s, err := data.Get()
if err != nil {
return
}
*v = s == "1"
}))
if *v {
data.Set("1")
} else {
data.Set("0")
}
en.Bind(data)
case *[]string:
data := binding.NewString()
data.Set(strings.Join(*v, ","))
en.Bind(data)
default:
}
return en
}
func newDefaultEntryCallback(value *string, callback func()) *widget.Entry {
en := widget.NewEntry()
data := binding.BindString(value)
en.Bind(data)
en.OnChanged = func(_ string) { callback() }
return en
}
+86
View File
@@ -0,0 +1,86 @@
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"
"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 {
var (
// 基础表单
formBase = &widget.Form{}
info = &models.ExecFiles{BaseCard: models.NewBaseCard(
models.ToolTypeExecFiles,
repo.User.GetUserInfo(global.DB).ID,
)}
)
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: "暂未支持可不填"})
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() {
fmt.Println("info:", info)
err := service.ExecFile.Create(info, chooseDir)
if err != nil {
dialog.ShowInformation("创建失败", err.Error(), w)
return
}
dialog.ShowInformation("ok", "创建成功", w)
// 更换
time.AfterFunc(2*time.Second, func() { w.Close() })
})
buttonTest := widget.NewButton("测试", func() {
if info.Cmd == "" {
dialog.ShowInformation("测试失败", "未填写执行命令", w)
return
}
dir := filepath.Join(chooseDir, info.Pwd)
err := utils.File.WinOpenFile(dir, info.Cmd)
if err != nil {
dialog.ShowInformation("测试失败", err.Error(), w)
return
}
})
return container.NewBorder(container.NewHBox(widget.NewLabel("新建"), widget.NewSeparator()),
nil, nil, nil, container.NewScroll(
container.NewVBox(
formBase,
buttonDir,
buttonTest,
container.NewHBox(buttonSave),
)))
}
+41 -5
View File
@@ -2,6 +2,9 @@ package views
import ( import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"work_cation/models"
"work_cation/views/createView"
"work_cation/views/showView"
) )
// Tutorial 定义教程的数据结构 // Tutorial 定义教程的数据结构
@@ -14,16 +17,49 @@ type Tutorial struct {
var ( var (
// Tutorials 定义每个教程的元数据 // Tutorials 定义每个教程的元数据
Tutorials = map[string]Tutorial{ Tutorials = map[string]Tutorial{
"welcome": {"主页", "", UserViews, true}, "welcome": {"主页", "", mainUserViews, true},
"canvas": {"我的", "", myCardsViews, true}, "canvas": {"我的", "", allCardsViews, true},
"create": {"新建", "", CreateErlangCard, true}, "create": {"新建", "", allCreateCards, true},
"otherUsers": {"同事", "", otherUser, true},
"followUsers": {"关注", "", followUsers, true},
} }
// TutorialIndex 定义我们的教程应该如何在索引树中布局 // TutorialIndex 定义我们的教程应该如何在索引树中布局
TutorialIndex = map[string][]string{ TutorialIndex = map[string][]string{
"": {"welcome", "canvas", "create"}, "": {"welcome", "canvas", "followUsers", "otherUsers", "create"},
//"collections": {"list", "table", "tree"}, //"collections": {"list", "table", "tree"},
//"containers": {"apptabs", "border", "box", "center", "doctabs", "grid", "scroll", "split"}, //"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
}
)
type cardInfo struct {
Name string
createView func(w fyne.Window) fyne.CanvasObject
createSize fyne.Size
showView func(fyne.Window, *models.BaseCard) (fyne.CanvasObject, error)
updateView func(fyne.Window, *models.BaseCard) fyne.CanvasObject
}
var (
cardTypeMap = map[string]cardInfo{
models.ToolTypeNoVarErlang: {
"Erlang代码脚本",
nil,
fyne.NewSize(0, 0),
showView.ErlangCardNoVarView,
createView.UpdateErlangCard},
models.ToolTypeErlang: {
"Erlang代码脚本模版",
createView.CreateErlangCard,
fyne.NewSize(600, 400),
showView.ErlangCardView,
createView.UpdateErlangCard},
models.ToolTypeExecFiles: {
"自定义可执行工具",
createView.CreateExecFile,
fyne.NewSize(600, 520),
showView.ExecFileCardView,
nil},
} }
) )
+109
View File
@@ -0,0 +1,109 @@
package views
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"slices"
"work_cation/global"
"work_cation/models"
"work_cation/repo"
"work_cation/service"
)
func followUsers(w fyne.Window) fyne.CanvasObject {
gridWrap := container.NewGridWrap(fyne.NewSize(200, 190))
findService, err := service.Zeroconf.FindService()
if err != nil {
return widget.NewLabel(err.Error())
}
users, err := repo.UserFollow.All(global.DB)
if err != nil {
return widget.NewLabel(err.Error())
}
slices.SortFunc(users, func(a, b models.UserFollows) int {
return b.Sort - a.Sort
})
for _, user := range users {
index := slices.IndexFunc(findService, indexOnline2User(user.ID))
var onlineCopy *models.Online
if index != -1 {
onlineCopy = &findService[index]
}
var baseCardV fyne.CanvasObject
del := func() { gridWrap.Remove(baseCardV) }
var userCopy = user
baseCardV, err = itemFollowUserView(w, onlineCopy, &userCopy, del)
if err != nil {
continue
}
gridWrap.Add(baseCardV)
}
scroll := container.NewScroll(gridWrap)
return container.NewBorder(nil, nil, nil, nil, scroll)
}
func indexOnline2User(id string) func(models.Online) bool {
return func(item models.Online) bool {
return item.ID == id
}
}
func itemFollowUserView(w fyne.Window, data *models.Online, user *models.UserFollows, del func()) (fyne.CanvasObject, error) {
onlineShow := widget.NewLabel("离线")
go func() {
defer func() { _ = recover() }()
if data == nil {
data = &models.Online{ID: user.ID, Ip: user.Ip, Port: user.Port}
}
newUser, err := service.Client.GetUser(data)
if err != nil {
data = nil
return
}
if err == nil {
onlineShow.SetText("在线")
}
if err == nil && newUser.UpdateTx != user.UpdateTx {
// 更新用户信息
fmt.Println("更新用户信息:", user, newUser)
repo.UserFollow.UnFollow(global.DB, user)
repo.UserFollow.Follow(global.DB, &models.UserFollows{Users: *newUser})
}
}()
followButton := widget.NewButton("取消关注", func() {
err := repo.UserFollow.UnFollow(global.DB, user)
if err != nil {
dialog.ShowInformation("取消失败", err.Error(), w)
return
}
dialog.ShowInformation("", "取消成功", w)
del()
})
showErrButton := widget.NewButton("查看主页", func() {
if data == nil {
dialog.ShowInformation("查看失败", "用户未在线", w)
return
}
otherUserIndexWin(&user.Users, data)
})
maishuiButton := widget.NewButton("聊天", func() {
//if data == nil {
// dialog.ShowInformation("查看失败", "用户未在线", w)
// return
//}
OpenChat(user.Users)
})
card := widget.NewCard(user.Name, user.Ip, container.NewVBox(onlineShow,
container.NewHBox(showErrButton, followButton),
maishuiButton))
return card, nil
}
+483
View File
@@ -0,0 +1,483 @@
package views
import (
"bytes"
"context"
"errors"
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/gin-gonic/gin"
"image"
"image/color"
"image/draw"
"image/png"
"net/http"
"slices"
"sync"
"work_cation/global"
"work_cation/models"
"work_cation/pkg/utils"
"work_cation/service"
)
type GameConfig struct {
Name string
X int
Y int
Win int
}
var GameConfigList = []GameConfig{
{"3*3 井字棋", 3, 3, 3},
{"10*10 五子棋", 10, 10, 5},
{"19*19 五子棋", 19, 19, 5},
}
func TenChinaGameView() {
game := NewTenGame(nil)
game.Show()
}
func StartGameListen() {
router := service.Server.Router
// 推送游戏邀请
service.Server.Router.POST("/send_game", func(c *gin.Context) {
user, msg, is := service.PublicPostCheck(c)
if !is {
return
}
w := fyne.CurrentApp().NewWindow("游戏邀请")
w.SetContent(container.NewVBox(
widget.NewLabel(fmt.Sprintf("%s向你发起了 %s 游戏邀请 ", user.Name, msg["name"])),
container.NewHBox(
widget.NewButton("同意", func() {
game := NewTenGame(nil)
game.itemX = float32(msg["x"].(float64))
game.itemY = float32(msg["y"].(float64))
game.winSum = int(msg["win"].(float64))
online := &models.Online{ID: user.ID, Ip: user.Ip, Port: user.Port}
err := game.AddNet(utils.Uuid.CreateUUID(), 0, online, &user.Users)
if err != nil {
dialog.ShowInformation("错误", err.Error(), w)
return
}
game.Show()
w.Close()
}),
widget.NewButton("婉拒", w.Close),
)))
w.CenterOnScreen()
w.RequestFocus()
w.Show()
c.JSON(http.StatusOK, gin.H{})
})
// 对方同意开始游戏
router.POST("/start_game", func(c *gin.Context) {
user, msg, is := service.PublicPostCheck(c)
if !is {
return
}
game := NewTenGame(nil)
game.itemX = float32(msg["x"].(float64))
game.itemY = float32(msg["y"].(float64))
game.winSum = int(msg["win"].(float64))
gameUuid := msg["game_uuid"].(string)
online := &models.Online{ID: user.ID, Ip: user.Ip, Port: user.Port}
game.AddNet(gameUuid, 1, online, &user.Users)
game.Show()
c.JSON(200, gin.H{"message": "ok"})
})
router.POST("/play_game", func(c *gin.Context) {
_, msg, is := service.PublicPostCheck(c)
if !is {
return
}
g := global.GetGameInfo(msg["game_uuid"].(string))
if g != nil {
game := g.Obj.(*TenGame)
game.Play(game.userIndex, int(msg["pos"].(float64)))
}
c.JSON(200, gin.H{"message": "ok"})
})
router.POST("/close_game", func(c *gin.Context) {
_, msg, is := service.PublicPostCheck(c)
if !is {
return
}
g := global.GetGameInfo(msg["game_uuid"].(string))
if g != nil {
dialog.NewConfirm("提示", "对方已退出", func(b bool) {
g.Obj.(*TenGame).w.Close()
}, g.Obj.(*TenGame).w).Show()
//dialog.ShowInformation("提示", "对方已退出", g.Obj.(*TenGame).w)
}
c.JSON(200, gin.H{"message": "ok"})
})
}
// 格子棋 Checkered-Chess
type TenGame struct {
lock sync.Mutex // 玩家操作锁
playerMax int // 玩家总人数
playerID2Image map[int]fyne.Resource // 各个玩家对应的图标
defaultImage fyne.Resource // 默认图标
currentRoundPlayer int // 当前行动的玩家
players [][]int // 各个玩家已完成下棋数据
allMaps []int // 所有下棋数据
winCallback func(int) // 胜利返回
Items []*canvas.Image // 各个板块对象
w fyne.Window
itemSize fyne.Size //单个格子大小
itemX float32 // 格子总数x
itemY float32 // 格子总数y
winSum int // 胜利连线数
Ctx context.Context
Cancel func()
isNet bool // 是否是网络模式
uuid string
myIndex int
user *models.Users //
userIndex int
online *models.Online
}
func NewTenGame(winCallback func(int)) *TenGame {
playerImageMap := map[int]fyne.Resource{
0: theme.RadioButtonIcon(),
1: theme.CancelIcon(),
//2: theme.DownloadIcon(),
}
ctx, cancel := context.WithCancel(context.Background())
game := &TenGame{
playerID2Image: playerImageMap,
defaultImage: nil, //theme.ViewFullScreenIcon(),
currentRoundPlayer: 0,
players: make([][]int, len(playerImageMap)),
winCallback: winCallback,
itemSize: fyne.NewSize(45, 45),
itemX: 19,
itemY: 19,
winSum: 5,
Ctx: ctx,
Cancel: cancel,
isNet: false,
}
return game
}
func (t *TenGame) AddNet(uuid string, myIndex int, online *models.Online, user *models.Users) error {
t.isNet = true
t.online = online
t.myIndex = myIndex
t.user = user
t.uuid = uuid
info := &global.RunGameInfo{
Uuid: t.uuid,
Type: global.GameType1,
Obj: t,
}
global.CreateGame(info)
// 发起人
if myIndex == 0 {
t.userIndex = 1
req := gin.H{
"type": global.GameType1,
"game_uuid": t.uuid,
"x": t.itemX,
"y": t.itemY,
"win": t.winSum,
}
_, err := service.PostSend("/start_game", online, req)
if err != nil {
t.isNet = false
return err
}
}
return nil
}
func (t *TenGame) Show() {
myApp := fyne.CurrentApp()
myWindow := myApp.NewWindow("")
t.w = myWindow
t.setTitle()
myWindow.SetContent(t.mainCanvasObject())
myWindow.SetOnClosed(func() {
if t.isNet {
global.DeleteGame(t.uuid)
service.Client.CloseGame(t.online, t.uuid)
}
})
myWindow.CenterOnScreen()
myWindow.RequestFocus()
myWindow.Show()
}
// 主界面
func (t *TenGame) mainCanvasObject() fyne.CanvasObject {
return container.NewBorder(nil, nil, nil, nil, t.chessCanvasObject())
}
// 棋盘界面
func (t *TenGame) chessCanvasObject() fyne.CanvasObject {
// 计算大小
wSize := fyne.NewSize(t.itemX*(t.itemSize.Width), t.itemY*(t.itemSize.Height))
var items []fyne.CanvasObject
for i := 0; i < int(t.itemX*t.itemY); i++ {
var itemIndex = i
image := canvas.NewImageFromResource(t.defaultImage)
image.FillMode = canvas.ImageFillOriginal
image.Resize(t.itemSize) //fy
t.Items = append(t.Items, image)
toggle := widget.NewButton("", func() {
err := t.Play(t.myIndex, itemIndex)
if err != nil {
dialog.ShowError(err, t.w)
}
})
toggle.Resize(t.itemSize)
iViews := container.NewWithoutLayout(image, toggle)
iViews.Resize(t.itemSize)
//gridWrap.Add()
items = append(items, iViews)
}
gridWrap := container.NewGridWithRows(int(t.itemX), items...)
scroll := container.NewScroll(gridWrap)
scroll.SetMinSize(wSize)
// 绘制背景
background := canvas.NewImageFromResource(drawABackgroundImage(wSize, int(t.itemX), int(t.itemY)))
background.SetMinSize(wSize)
return container.New(layout.NewCenterLayout(),
background,
scroll,
)
}
func (t *TenGame) Play(userIndex int, pos int) error {
t.lock.Lock()
defer t.lock.Unlock()
if t.isNet && t.currentRoundPlayer != userIndex {
return errors.New("你急个der")
}
if slices.Contains(t.allMaps, pos) {
return errors.New("已经下过了")
}
if t.isNet && t.myIndex == userIndex {
// todo 广播数据
_, err := service.Client.PlayGame(t.online, t.uuid, pos)
if err != nil {
return err
}
}
// 下棋
t.allMaps = append(t.allMaps, pos)
t.play(pos)
// 推进
t.advance()
return nil
}
// 推进
func (t *TenGame) advance() {
t.currentRoundPlayer++
if t.currentRoundPlayer+1 > len(t.playerID2Image) {
t.currentRoundPlayer = 0
}
t.setTitle()
}
func (t *TenGame) setTitle() {
title := fmt.Sprintf("让我们来下棋 %0.f*%0.f win:%d", t.itemX, t.itemY, t.winSum)
if t.isNet {
title += fmt.Sprintf(" | 我 vs %s", t.user.Name)
if t.currentRoundPlayer == t.myIndex {
title += " | 我的回合"
} else {
title += " | 对手回合"
}
}
t.w.SetTitle(title)
}
/*
1, 2, 3
4, 5, 6
7, 8, 9
*/
func (t *TenGame) play(pos int) {
// 更新玩家数据
playerIntList := t.players[t.currentRoundPlayer]
playerIntList = append(playerIntList, pos)
t.players[t.currentRoundPlayer] = playerIntList
// 刷新展示
t.Items[pos].Resource = t.playerID2Image[t.currentRoundPlayer]
t.Items[pos].Refresh()
// check 胜利 判断
if t.winCheck(playerIntList, pos) {
endView := widget.NewButton("关闭", func() {
t.w.Close()
})
img := canvas.NewImageFromResource(t.playerID2Image[t.currentRoundPlayer])
img.FillMode = canvas.ImageFillOriginal
v := container.NewBorder(nil, endView, nil, nil,
container.NewBorder(nil, nil, img, nil, widget.NewLabel("胜利")))
dialog.NewCustomWithoutButtons("游戏结束", v, t.w).Show()
if t.winCallback != nil {
t.winCallback(t.currentRoundPlayer)
}
} else if len(t.allMaps) == int(t.itemX*t.itemY) {
endView := widget.NewButton("关闭", t.w.Close)
img := canvas.NewImageFromResource(t.playerID2Image[t.currentRoundPlayer])
img.FillMode = canvas.ImageFillOriginal
v := container.NewBorder(nil, endView, nil, nil,
container.NewBorder(nil, nil, img, nil, widget.NewLabel("平局")))
dialog.NewCustomWithoutButtons("游戏结束", v, t.w).Show()
}
}
func (t *TenGame) winCheck(playerIntList []int, pos int) bool {
//slices.Sort(playerIntList)
return IsArithmeticSequence2(playerIntList, pos, t.winSum, int(t.itemX), int(t.itemY))
}
/*
0, 1, 2
3, 4, 5
6, 7, 8
*/
type pos struct {
x int
y int
}
/*
{0, 0}, {1, 0}, {2, 0}
{0, 1}, {1, 1}, {2, 1}
{0, 2}, {1, 2}, {2, 2}
0 1 2
3 4 5
6 7 8
*/
func int3pos(maxX, i int) pos {
return pos{x: i % maxX, y: i / maxX}
}
func pos2int(maxX int, pos pos) int {
return pos.x + pos.y*maxX
}
var lineDirection = [][]pos{
{{1, 1}, {-1, -1}},
{{1, -1}, {-1, 1}},
{{1, 0}, {-1, 0}},
{{0, 1}, {0, -1}},
}
func IsArithmeticSequence2(numbers []int, in int, diff int, mapX, mapY int) bool {
if len(numbers) <= diff-1 {
return false
}
inPos := int3pos(mapX, in)
for _, direction := range lineDirection {
var sum = 0
for _, idirec := range direction {
sum += lineSum(inPos, mapX, mapY, numbers, idirec)
}
if sum >= diff-1 {
return true
}
}
return false
}
func lineSum(pos pos, mapX, mapY int, numbers []int, direction pos) (sum int) {
for {
pos.x += direction.x
pos.y += direction.y
if pos.x >= mapX || pos.y >= mapY || pos.x < 0 || pos.y < 0 {
return
}
if !slices.Contains(numbers, pos2int(mapX, pos)) {
return
}
sum++
}
}
// 画背景图
func drawABackgroundImage(size fyne.Size, itemX, itemY int) fyne.Resource {
img := image.NewRGBA(image.Rect(0, 0, int(size.Width), int(size.Height)))
draw.Draw(img, img.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
//itemX = itemX * 2
//itemY = itemY * 2
// 画横线
for ix := 1; ix < itemX; ix++ {
i1 := int(size.Height * (float32(ix) / float32(itemX)))
for x := 1; x < int(size.Width); x++ {
img.Set(x, i1, color.Black)
}
//ix++
}
// Draw vertical line
for ix := 1; ix < itemY; ix++ {
i2 := int(size.Width * (float32(ix) / float32(itemY)))
for y := 0; y < int(size.Height); y++ {
img.Set(i2, y, color.Black)
}
//ix++
}
buffer := new(bytes.Buffer)
err := png.Encode(buffer, img)
if err != nil {
return nil
}
return &fyne.StaticResource{
StaticName: "",
StaticContent: buffer.Bytes(),
}
}
+125
View File
@@ -0,0 +1,125 @@
package views
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"time"
"work_cation/global"
"work_cation/models"
"work_cation/repo"
"work_cation/service"
)
func otherUser(w fyne.Window) fyne.CanvasObject {
gridWrap := container.NewGridWrap(fyne.NewSize(200, 190))
findService, err := service.Zeroconf.FindService()
if err != nil {
return widget.NewLabel("网络异常请稍后尝试")
}
go func() {
for _, online := range findService {
var (
onlineCopy = online
)
baseCardV, err := itemOnlineUserView(w, &onlineCopy)
if err != nil {
continue
}
gridWrap.Add(baseCardV)
}
}()
scroll := container.NewScroll(gridWrap)
return container.NewBorder(nil, nil, nil, nil, scroll)
}
func itemOnlineUserView(w fyne.Window, data *models.Online) (fyne.CanvasObject, error) {
u := repo.UserFollow.GetUser(global.DB, data.ID)
var followLabel *widget.Label
const (
noFollow = "未关注"
isFollow = "已关注"
)
if u == nil {
followLabel = widget.NewLabel(noFollow)
} else {
followLabel = widget.NewLabel(isFollow)
}
user, err := service.Client.GetUser(data)
if err != nil {
return nil, err
}
followButton := widget.NewButton("关注", func() {
if followLabel.Text == isFollow {
dialog.ShowInformation("结果", "已关注成功", w)
return
}
err = repo.UserFollow.Follow(global.DB, &models.UserFollows{Users: *user})
if err != nil {
dialog.ShowInformation("关注失败", err.Error(), w)
return
}
dialog.ShowInformation("结果", "关注成功", w)
followLabel.SetText(isFollow)
})
showErrButton := widget.NewButton("查看主页", func() {
otherUserIndexWin(user, data)
})
card := widget.NewCard(user.Name, user.Ip, container.NewVBox(followLabel, showErrButton, followButton))
return card, nil
}
func otherUserIndexWin(user *models.Users, data *models.Online) {
myApp := fyne.CurrentApp()
myWindow := myApp.NewWindow(user.Name)
myWindow.CenterOnScreen()
myWindow.Resize(fyne.NewSize(620, 500))
myWindow.SetContent(otherUserIndexView(myWindow, data))
myWindow.Show()
}
func otherUserIndexView(w fyne.Window, online *models.Online) fyne.CanvasObject {
gridWrap := container.NewGridWrap(fyne.NewSize(200, 200))
cards, _ := service.Client.GetCards(online)
for _, baseCard := range cards {
var baseCardCopy = baseCard
baseCardV := baseOtherCardView(w, baseCardCopy, online)
gridWrap.Add(baseCardV)
}
scroll := container.NewScroll(gridWrap)
return container.NewBorder(nil, nil, nil, nil, scroll)
}
func baseOtherCardView(w fyne.Window, baseCardCopy models.BaseCard, online *models.Online) *widget.Card {
saveCard := repo.BaseCard.Find(global.DB, baseCardCopy.UUID)
infoWid := container.NewVBox(
widget.NewLabel(baseCardCopy.ToolType),
widget.NewLabel(fmt.Sprintf("本地:%s", saveCard.UpdateTx.Format(time.DateTime))),
widget.NewLabel(fmt.Sprintf("作者:%s", baseCardCopy.UpdateTx.Format(time.DateTime))),
)
baseCardV := widget.NewCard(baseCardCopy.Title, baseCardCopy.Text, container.NewBorder(nil, widget.NewToolbar(
widget.NewToolbarAction(theme.DownloadIcon(), func() {
b := saveCard.UpdateTx.Unix() == baseCardCopy.UpdateTx.Unix()
if b {
dialog.ShowInformation("结果", "已下载本地", w)
return
}
// 下载到本地 TODO 若没关注自动关注
service.BaseCard.DownloadCard(w, baseCardCopy, online)
}),
), nil, nil, infoWid))
return baseCardV
}
+87
View File
@@ -0,0 +1,87 @@
package views
import (
"fmt"
"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/service"
)
// 卡片展示界面
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) {
if info, is := cardTypeMap[baseCard.ToolType]; is {
var baseCardCopy = baseCard
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)
}
}
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() {
dialog.NewConfirm("提示", fmt.Sprintf("确定删除:%s?", baseCardCopy.Title), func(b bool) {
if b {
err := service.BaseCard.Delete(global.DB, baseCardCopy)
if err != nil {
dialog.ShowInformation("删除失败", err.Error(), w)
return
}
gridWrap.Remove(baseCardV)
}
}, w).Show()
}),
widget.NewToolbarAction(theme.SettingsIcon(), func() {
info := cardTypeMap[baseCardCopy.ToolType]
if info.updateView == nil {
return
}
cWin := fyne.CurrentApp().NewWindow(baseCardV.Title + " 设置")
cWin.SetContent(info.updateView(cWin, &baseCardCopy))
cWin.Resize(info.createSize)
cWin.CenterOnScreen()
cWin.Show()
}),
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
}
@@ -1,61 +1,67 @@
package views package showView
import ( import (
"fmt" "encoding/json"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"github.com/atotto/clipboard" "github.com/atotto/clipboard"
"os"
"path/filepath"
"strings" "strings"
"work_cation/global" "work_cation/cfg"
"work_cation/models" "work_cation/models"
"work_cation/models/repo"
"work_cation/pkg/utils"
) )
func myCardsViews(w fyne.Window) fyne.CanvasObject { func ErlangCardNoVarView(w fyne.Window, baseInfo *models.BaseCard) (fyne.CanvasObject, error) {
var items []fyne.CanvasObject button := widget.NewButton("复制", func() {
for _, i := range repo.ErlangCardRepo.FindAll(global.DB) { // 读取类型文件数据
var i2 = i infoPath := filepath.Join(cfg.T.CardDir, baseInfo.UUID, cfg.T.CardInfo)
items = append(items, ErlangCardView(w, &i2)) bytes, err := os.ReadFile(infoPath)
} if err != nil {
dialog.ShowError(err, w)
return container.NewBorder(nil, nil, nil, nil, container.NewScroll( return
container.NewGridWrap(fyne.NewSize(200, 200), items...))) }
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
} }
func ErlangCardView(_ fyne.Window, data *models.ErlangCards) fyne.CanvasObject { func ErlangCardView(w fyne.Window, baseInfo *models.BaseCard) (fyne.CanvasObject, error) {
if data == nil {
data = &models.ErlangCards{
UUID: utils.CreateUUID(),
Title: "玩家获取",
Text: "玩家获取脚本",
VarName: []string{"服务器ID", "玩家UID"},
Template: "z_db_lib:get(z_db_lib:get_table('role', $var), $var).",
}
}
if len(data.VarName) == 0 {
return widget.NewCard(data.Title, data.Text, widget.NewButton(data.Template, func() {
fmt.Print("复制:", data.UUID)
}))
}
txtBound := binding.NewString()
txtBound.Set(data.Template)
txtWid := widget.NewEntryWithData(txtBound)
txtWid.Wrapping = fyne.TextWrapOff
cardButton := widget.NewButton("OPEN", func() { cardButton := widget.NewButton("OPEN", func() {
go UseErlangCard(data) // 读取类型文件数据
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)
}) })
card := widget.NewCard(data.Title, data.Text, cardButton)
//image := canvas.NewImageFromResource(assets.LogoDataSR) //image := canvas.NewImageFromResource(assets.LogoDataSR)
//image.FillMode = canvas.ImageFillContain //image.FillMode = canvas.ImageFillContain
//card.SetImage(image) //card.SetImage(image)
return card return cardButton, nil
} }
func UseErlangCard(data *models.ErlangCards) { func UseErlangCard(data *models.ErlangCards) {
@@ -140,6 +146,7 @@ func replaceVars(input string, replacements []string) string {
result = strings.Replace(result, "$var", replacements[varIndex], 1) result = strings.Replace(result, "$var", replacements[varIndex], 1)
varIndex++ varIndex++
} }
cleanedText := strings.ReplaceAll(result, "\r", "")
return result cleanedText = strings.ReplaceAll(cleanedText, "\n", "")
return cleanedText
} }
+61
View File
@@ -0,0 +1,61 @@
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"
"work_cation/pkg/utils"
)
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 := container.NewVBox(cardButton, openDirButton)
return card, nil
}
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
}
dir := filepath.Join(cfg.T.CardDir, data.UUID, info.Pwd)
go func() {
err = utils.File.WinOpenFile(dir, info.Cmd)
if err != nil {
dialog.ShowInformation("打开失败", err.Error(), w)
return
}
}()
}
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)
}
}()
}
+112 -14
View File
@@ -1,32 +1,130 @@
package views package views
import ( import (
"fmt"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"os"
"path/filepath"
"time"
"work_cation/assets" "work_cation/assets"
"work_cation/cfg"
"work_cation/global" "work_cation/global"
"work_cation/models/repo" "work_cation/models"
"work_cation/pkg/utils"
"work_cation/repo"
"work_cation/service"
) )
func UserViews(w fyne.Window) fyne.CanvasObject { func mainUserViews(w fyne.Window) fyne.CanvasObject {
image := canvas.NewImageFromResource(assets.LogoDataSR)
image.FillMode = canvas.ImageFillContain
user := repo.UserRepo.GetUserInfo(global.DB) var userCard = widget.NewCard("", "", nil)
user1 := repo.User.GetUserInfo(global.DB)
userCard := widget.NewCard( refresh := func(user *models.Users) {
user.Name, userCard.SetTitle(user.Name)
user.Ip, userCard.SetSubTitle(user.Ip)
nil) image := canvas.NewImageFromResource(assets.LogoDataSR)
userCard.SetImage(image) if user.Avatar != "" {
bytes, err := os.ReadFile(filepath.Join(cfg.T.Avatar, user.Avatar))
return container.NewBorder(nil, if err == nil {
image.Resource = fyne.NewStaticResource("", bytes)
}
}
image.FillMode = canvas.ImageFillContain
userCard.SetImage(image)
var status = ""
if service.Server.Status == service.StatusOnline {
status = "在线"
} else {
status = "离线"
}
v := container.NewVBox(
widget.NewLabel(fmt.Sprintf("状态:%s", status)),
widget.NewButton("游戏", TenChinaGameView),
)
userCard.SetContent(v)
}
refresh(user1)
return container.NewBorder(widget.NewToolbar(
widget.NewToolbarAction(theme.SettingsIcon(), func() { mainUserSetWin(refresh) }),
widget.NewToolbarAction(theme.LoginIcon(), func() {
err := service.Server.Online()
if err != nil {
dialog.ShowInformation("登录失败", err.Error(), w)
return
}
refresh(user1)
}),
widget.NewToolbarAction(theme.LogoutIcon(), func() {
err := service.Server.StatusOffline()
if err != nil {
dialog.ShowInformation("离线失败", err.Error(), w)
return
}
refresh(user1)
}),
),
nil, nil,
nil, nil,
nil, nil,
userCard, userCard,
) )
}
func mainUserSetWin(refresh func(*models.Users)) {
myApp := fyne.CurrentApp()
myWindow := myApp.NewWindow("设置个人信息")
myWindow.CenterOnScreen()
myWindow.Resize(fyne.NewSize(500, 0))
myWindow.SetContent(mainUserSetView(myWindow, refresh))
myWindow.Show()
}
func mainUserSetView(w fyne.Window, refresh func(*models.Users)) fyne.CanvasObject {
var (
formWid = widget.NewForm()
user = repo.User.GetUserInfo(global.DB)
avatar = ""
)
formWid.SubmitText = "保存"
formWid.OnSubmit = func() {
if avatar != "" {
avatarPath := fmt.Sprintf("%s_avatar%s", user.ID, filepath.Ext(avatar))
// 删除旧的
if user.Avatar != "" {
err := os.Remove(filepath.Join(cfg.T.Avatar, user.Avatar))
if err != nil {
dialog.ShowInformation("原头像删除错误", err.Error(), w)
}
}
// 拷贝文件
err := utils.File.CopyFile(avatar, filepath.Join(cfg.T.Avatar, avatarPath))
if err != nil {
dialog.ShowInformation("头像地址错误", err.Error(), w)
return
}
avatar = ""
user.Avatar = avatarPath
}
err := repo.User.Update(global.DB, user)
if err != nil {
dialog.ShowInformation("保存失败", err.Error(), w)
return
}
dialog.ShowInformation("结果", "保存成功", w)
refresh(user)
time.AfterFunc(200*time.Millisecond, w.Close)
}
formWid.Append("用户名", widget.NewEntryWithData(binding.BindString(&user.Name)))
formWid.Append("头像", widget.NewEntryWithData(binding.BindString(&avatar)))
formWid.Append("邮箱", widget.NewEntryWithData(binding.BindString(&user.Email)))
formWid.Append("电话", widget.NewEntryWithData(binding.BindString(&user.Phone)))
formWid.Append("工位", widget.NewEntryWithData(binding.BindString(&user.Address)))
return formWid
} }