Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ec023e1c2 | |||
| 83dfff5aa4 | |||
| 8a8a6e0bfa | |||
| b27d2b80f7 | |||
| 263a57f636 | |||
| 2fb887a936 | |||
| 411876b377 | |||
| 7986bb1c9b | |||
| 2f8d833006 | |||
| b3cf948fd2 | |||
| 245875881c | |||
| 99ed84f534 | |||
| 88439dfb81 | |||
| 7a6f815597 | |||
| e42744d681 | |||
| 7862b1d88e | |||
| e6f5aaa02f | |||
| d1d7f56ce4 | |||
| 0716c82833 | |||
| 1a0a9466d8 | |||
| 3b69ba93ad | |||
| a0e898e46c | |||
| a77308573e | |||
| 250ffb4e20 |
@@ -4,3 +4,4 @@
|
||||
/work_cation.iml
|
||||
/work_cation_0.1.2.zip
|
||||
/data.db
|
||||
/data/
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ var _ fyne.Theme = (*MyTheme)(nil)
|
||||
//go:embed simkai.ttf
|
||||
var arialNovaLight []byte
|
||||
|
||||
//go:embed logo.jpg
|
||||
//go:embed wolf_logo.png
|
||||
var logoData []byte
|
||||
|
||||
var LogoDataSR = &fyne.StaticResource{
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
+20
-2
@@ -3,17 +3,35 @@ package cfg
|
||||
import (
|
||||
"fmt"
|
||||
"gopkg.in/ini.v1"
|
||||
"os"
|
||||
)
|
||||
|
||||
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{
|
||||
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() {
|
||||
defer func() {
|
||||
_ = os.Mkdir(T.CardDir, os.ModeDir)
|
||||
_ = os.Mkdir(T.Avatar, os.ModeDir)
|
||||
}()
|
||||
cfg, err := ini.Load("conf.ini")
|
||||
if err != nil {
|
||||
fmt.Println("读取配置错误1:", err)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
@@ -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
@@ -4,8 +4,8 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"work_cation/cfg"
|
||||
"work_cation/models"
|
||||
"work_cation/models/repo"
|
||||
"work_cation/pkg/gormx"
|
||||
"work_cation/repo"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
@@ -17,14 +17,15 @@ func InitDB() {
|
||||
panic(err)
|
||||
}
|
||||
err = DB.AutoMigrate(
|
||||
&models.ErlangCards{},
|
||||
&models.ErlangCardCollections{},
|
||||
&models.History{},
|
||||
&models.Users{},
|
||||
&models.UserFollows{},
|
||||
&models.BaseCard{},
|
||||
&models.ChatMessage{},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
repo.UserRepo.GetUserInfo(DB)
|
||||
repo.User.GetUserInfo(DB)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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),
|
||||
}
|
||||
@@ -17,39 +17,67 @@ require (
|
||||
require (
|
||||
fyne.io/systray v1.11.0 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect
|
||||
github.com/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/fredbi/uri v1.1.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/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // 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/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-text/render v0.1.1-0.20240418202334-dd62631dae9b // 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/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/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // 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/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/mattn/go-isatty v0.0.20 // 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/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/rymdport/portal v0.2.6 // indirect
|
||||
github.com/sqweek/dialog v0.0.0-20240226140203-065105509627 // indirect
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
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
|
||||
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/mobile v0.0.0-20231127183840-76ac6878050a // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.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/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -45,6 +45,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf h1:FPsprx82rdrX2jiKyS17BH6IrTmUBYqZa/CXT4uvb+I=
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
@@ -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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
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/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/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
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-20200629203442-efcf912fb354/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/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/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/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/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
||||
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-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-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/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
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-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/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.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
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/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||
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/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=
|
||||
@@ -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/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.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.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
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/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/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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
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/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/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||
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/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.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/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
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/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=
|
||||
@@ -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.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-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 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/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=
|
||||
@@ -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/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
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/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/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/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||
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/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||
github.com/sqweek/dialog v0.0.0-20240226140203-065105509627 h1:2JL2wmHXWIAxDofCK+AdkFi1KEg3dgkefCsm7isADzQ=
|
||||
github.com/sqweek/dialog v0.0.0-20240226140203-065105509627/go.mod h1:/qNPSY91qTz/8TgHEMioAUc6q7+3SOybeKczHMXFcXw=
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
||||
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.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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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.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.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/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/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.27/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/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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-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.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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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-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-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-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=
|
||||
@@ -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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
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-20190226205417-e64efc72b421/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-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-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-20191005200804-aed5e4c7ecf9/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-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.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/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/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=
|
||||
@@ -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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
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-20190308202827-9d24e82272b4/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-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-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-20191227053925-7b8e75db28f4/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.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.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 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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-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=
|
||||
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/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/app"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"work_cation/assets"
|
||||
"work_cation/global"
|
||||
"work_cation/service"
|
||||
"work_cation/version"
|
||||
"work_cation/views"
|
||||
)
|
||||
|
||||
// 本地缓存 上次打开目录
|
||||
const preferenceCurrentTutorial = "currentTutorial"
|
||||
|
||||
func main() {
|
||||
@@ -21,7 +22,7 @@ func main() {
|
||||
a := app.NewWithID("io.fyne.workCation")
|
||||
a.Settings().SetTheme(&assets.MyTheme{})
|
||||
a.SetIcon(assets.LogoDataSR)
|
||||
w := a.NewWindow("主页 " + version.NowVersion)
|
||||
w := a.NewWindow("开发小工具 " + version.NowVersion)
|
||||
w.Resize(fyne.NewSize(800, 550))
|
||||
w.SetContent(mainView(w))
|
||||
w.CenterOnScreen()
|
||||
@@ -33,6 +34,13 @@ func main() {
|
||||
|
||||
// 主界面
|
||||
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 (
|
||||
content = container.NewMax()
|
||||
a = fyne.CurrentApp()
|
||||
@@ -102,13 +110,14 @@ func makeNav(setTutorial func(tutorial views.Tutorial), loadPrevious bool) fyne.
|
||||
if unsupportedTutorial(t) {
|
||||
return
|
||||
}
|
||||
// 存储默认打开
|
||||
a.Preferences().SetString(preferenceCurrentTutorial, uid)
|
||||
fmt.Println(t)
|
||||
setTutorial(t)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// 读取存储默认打开
|
||||
if loadPrevious {
|
||||
currentPref := a.Preferences().StringWithFallback(preferenceCurrentTutorial, "welcome")
|
||||
tree.Select(currentPref)
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,9 @@ package models
|
||||
import "work_cation/pkg/gormx"
|
||||
|
||||
type ErlangCards struct {
|
||||
UUID string `json:"uuid"` // id ip加时间戳生成
|
||||
UserIp string `json:"user_ip"` // 用户ip
|
||||
Title string `json:"title"` // 标题
|
||||
Text string `json:"text"` // 内容
|
||||
Covers gormx.ListString `json:"covers"` // 封面
|
||||
BaseCard
|
||||
Template string `json:"template"` // 模版
|
||||
VarName gormx.ListString `json:"var_name"` // 变量
|
||||
VarContent gormx.ListString `json:"var_content"` // 变量内容
|
||||
IsShowOut bool `json:"is_show_out"` // 是否展示
|
||||
|
||||
Number int `json:"number"` // 数量
|
||||
Goods int `json:"goods"` // 点赞
|
||||
Collection int `json:"collection"` // 收藏
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package models
|
||||
|
||||
type ExecFiles struct {
|
||||
BaseCard
|
||||
// 文件名
|
||||
Cmd string `json:"common"` // 执行命令
|
||||
Pwd string `json:"pwd"` // 执行路径
|
||||
Files []string `json:"files"` // 原始文件
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
//}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+7
-2
@@ -1,11 +1,16 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type Users struct {
|
||||
Ip string `gorm:"primarykey" json:"ip"`
|
||||
ID string `gorm:"primarykey" json:"id"`
|
||||
Ip string `json:"ip"`
|
||||
UpdateTx time.Time `json:"update_tx"`
|
||||
Port string `json:"port"`
|
||||
Name string `json:"name"` // 昵称
|
||||
Avatar string `json:"avatar"` // 头像
|
||||
Cover string `json:"cover"` // 封面
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Address string `json:"address"`
|
||||
Address string `json:"address"` // 工位
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ package models
|
||||
// UserFollows 关注用户
|
||||
type UserFollows struct {
|
||||
Users
|
||||
Sort int
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -9,19 +9,37 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var uuidint = 0
|
||||
var lock sync.Mutex
|
||||
type uuidUtil struct {
|
||||
lock sync.Mutex
|
||||
uuidint int
|
||||
}
|
||||
|
||||
func CreateUUID() string {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
var Uuid = &uuidUtil{}
|
||||
|
||||
func (u *uuidUtil) CreateUUID() string {
|
||||
u.lock.Lock()
|
||||
defer u.lock.Unlock()
|
||||
// 生成基于字符串的 UUID
|
||||
key := fmt.Sprintln(Get192Ip(), time.Now().Format("2006-01-02_15-04-05"), uuidint)
|
||||
uuidint++
|
||||
key := fmt.Sprintln(IP.Get192Ip(), time.Now().Format("2006-01-02_15-04-05"), u.uuidint)
|
||||
u.uuidint++
|
||||
u1 := uuid.NewSHA1(uuid.Nil, []byte(key))
|
||||
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 {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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("完成 请去[我的]查看")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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...))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -2,6 +2,9 @@ package views
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"work_cation/models"
|
||||
"work_cation/views/createView"
|
||||
"work_cation/views/showView"
|
||||
)
|
||||
|
||||
// Tutorial 定义教程的数据结构
|
||||
@@ -14,16 +17,49 @@ type Tutorial struct {
|
||||
var (
|
||||
// Tutorials 定义每个教程的元数据
|
||||
Tutorials = map[string]Tutorial{
|
||||
"welcome": {"主页", "", UserViews, true},
|
||||
"canvas": {"我的", "", myCardsViews, true},
|
||||
"create": {"新建", "", CreateErlangCard, true},
|
||||
"welcome": {"主页", "", mainUserViews, true},
|
||||
"canvas": {"我的", "", allCardsViews, true},
|
||||
"create": {"新建", "", allCreateCards, true},
|
||||
"otherUsers": {"同事", "", otherUser, true},
|
||||
"followUsers": {"关注", "", followUsers, true},
|
||||
}
|
||||
|
||||
// TutorialIndex 定义我们的教程应该如何在索引树中布局
|
||||
TutorialIndex = map[string][]string{
|
||||
"": {"welcome", "canvas", "create"},
|
||||
"": {"welcome", "canvas", "followUsers", "otherUsers", "create"},
|
||||
//"collections": {"list", "table", "tree"},
|
||||
//"containers": {"apptabs", "border", "box", "center", "doctabs", "grid", "scroll", "split"},
|
||||
//"widgets": {"accordion", "button", "card", "entry", "form", "input", "progress", "text", "toolbar"},
|
||||
//"widgets": {"accordion", "button", "card", "entry", "form", "input", "progress", "text", "toolbar"}, 58 * 3 + 106 = 280
|
||||
}
|
||||
)
|
||||
|
||||
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},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/data/binding"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"github.com/atotto/clipboard"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"work_cation/global"
|
||||
"work_cation/cfg"
|
||||
"work_cation/models"
|
||||
"work_cation/models/repo"
|
||||
"work_cation/pkg/utils"
|
||||
)
|
||||
|
||||
func myCardsViews(w fyne.Window) fyne.CanvasObject {
|
||||
var items []fyne.CanvasObject
|
||||
for _, i := range repo.ErlangCardRepo.FindAll(global.DB) {
|
||||
var i2 = i
|
||||
items = append(items, ErlangCardView(w, &i2))
|
||||
func ErlangCardNoVarView(w fyne.Window, baseInfo *models.BaseCard) (fyne.CanvasObject, error) {
|
||||
button := widget.NewButton("复制", func() {
|
||||
// 读取类型文件数据
|
||||
infoPath := filepath.Join(cfg.T.CardDir, baseInfo.UUID, cfg.T.CardInfo)
|
||||
bytes, err := os.ReadFile(infoPath)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
|
||||
return container.NewBorder(nil, nil, nil, nil, container.NewScroll(
|
||||
container.NewGridWrap(fyne.NewSize(200, 200), items...)))
|
||||
var data models.ErlangCards
|
||||
err = json.Unmarshal(bytes, &data)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
|
||||
func ErlangCardView(_ fyne.Window, data *models.ErlangCards) fyne.CanvasObject {
|
||||
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).",
|
||||
cleanedText := strings.ReplaceAll(data.Template, "\r", "")
|
||||
cleanedText = strings.ReplaceAll(cleanedText, "\n", "")
|
||||
err = clipboard.WriteAll(cleanedText)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
}
|
||||
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() {
|
||||
go UseErlangCard(data)
|
||||
dialog.NewInformation("复制脚本成功", data.Template, w).Show()
|
||||
})
|
||||
return button, nil
|
||||
}
|
||||
|
||||
card := widget.NewCard(data.Title, data.Text, cardButton)
|
||||
|
||||
func ErlangCardView(w fyne.Window, baseInfo *models.BaseCard) (fyne.CanvasObject, error) {
|
||||
cardButton := widget.NewButton("OPEN", func() {
|
||||
// 读取类型文件数据
|
||||
infoPath := filepath.Join(cfg.T.CardDir, baseInfo.UUID, cfg.T.CardInfo)
|
||||
bytes, err := os.ReadFile(infoPath)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
var data models.ErlangCards
|
||||
err = json.Unmarshal(bytes, &data)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
go UseErlangCard(&data)
|
||||
})
|
||||
//image := canvas.NewImageFromResource(assets.LogoDataSR)
|
||||
//image.FillMode = canvas.ImageFillContain
|
||||
//card.SetImage(image)
|
||||
return card
|
||||
return cardButton, nil
|
||||
}
|
||||
|
||||
func UseErlangCard(data *models.ErlangCards) {
|
||||
@@ -140,6 +146,7 @@ func replaceVars(input string, replacements []string) string {
|
||||
result = strings.Replace(result, "$var", replacements[varIndex], 1)
|
||||
varIndex++
|
||||
}
|
||||
|
||||
return result
|
||||
cleanedText := strings.ReplaceAll(result, "\r", "")
|
||||
cleanedText = strings.ReplaceAll(cleanedText, "\n", "")
|
||||
return cleanedText
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
+110
-12
@@ -1,32 +1,130 @@
|
||||
package views
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
"work_cation/assets"
|
||||
"work_cation/cfg"
|
||||
"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 {
|
||||
|
||||
var userCard = widget.NewCard("", "", nil)
|
||||
user1 := repo.User.GetUserInfo(global.DB)
|
||||
refresh := func(user *models.Users) {
|
||||
userCard.SetTitle(user.Name)
|
||||
userCard.SetSubTitle(user.Ip)
|
||||
image := canvas.NewImageFromResource(assets.LogoDataSR)
|
||||
if user.Avatar != "" {
|
||||
bytes, err := os.ReadFile(filepath.Join(cfg.T.Avatar, user.Avatar))
|
||||
if err == nil {
|
||||
image.Resource = fyne.NewStaticResource("", bytes)
|
||||
}
|
||||
}
|
||||
image.FillMode = canvas.ImageFillContain
|
||||
|
||||
user := repo.UserRepo.GetUserInfo(global.DB)
|
||||
|
||||
userCard := widget.NewCard(
|
||||
user.Name,
|
||||
user.Ip,
|
||||
nil)
|
||||
userCard.SetImage(image)
|
||||
|
||||
return container.NewBorder(nil,
|
||||
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,
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user