diff --git a/.gitignore b/.gitignore index e415740..6db6d1e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /work_cation.exe /work_cation.iml /work_cation_0.1.2.zip +/data.db diff --git a/cfg/config.go b/cfg/config.go new file mode 100644 index 0000000..29241c4 --- /dev/null +++ b/cfg/config.go @@ -0,0 +1,27 @@ +package cfg + +import ( + "fmt" + "gopkg.in/ini.v1" +) + +type Config struct { + DbPath string `ini:"dbPath"` +} + +var T = Config{ + DbPath: "./data.db", +} + +func init() { + cfg, err := ini.Load("conf.ini") + if err != nil { + fmt.Println("读取配置错误1:", err) + return + } + err = cfg.MapTo(&T) + if err != nil { + fmt.Println("读取配置错误2:", err) + return + } +} diff --git a/global/data.go b/global/data.go new file mode 100644 index 0000000..0f144a8 --- /dev/null +++ b/global/data.go @@ -0,0 +1,30 @@ +package global + +import ( + "gorm.io/gorm" + "work_cation/cfg" + "work_cation/models" + "work_cation/models/repo" + "work_cation/pkg/gormx" +) + +var DB *gorm.DB + +func InitDB() { + var err error + DB, err = gormx.New(gormx.Config{DSN: cfg.T.DbPath, Type: "sqlite3"}) + if err != nil { + panic(err) + } + err = DB.AutoMigrate( + &models.ErlangCards{}, + &models.ErlangCardCollections{}, + &models.History{}, + &models.Users{}, + &models.UserFollows{}, + ) + if err != nil { + panic(err) + } + repo.UserRepo.GetUserInfo(DB) +} diff --git a/go.mod b/go.mod index cabfcfb..01a72c5 100644 --- a/go.mod +++ b/go.mod @@ -2,12 +2,21 @@ module work_cation go 1.21.1 -require fyne.io/fyne/v2 v2.5.1 +require ( + fyne.io/fyne/v2 v2.5.1 + github.com/atotto/clipboard v0.1.4 + github.com/google/uuid v1.6.0 + github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible + github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 + github.com/sirupsen/logrus v1.8.1 + gorm.io/driver/mysql v1.5.7 + gorm.io/driver/sqlite v1.5.6 + gorm.io/gorm v1.25.12 +) require ( fyne.io/systray v1.11.0 // indirect github.com/BurntSushi/toml v1.4.0 // indirect - github.com/atotto/clipboard v0.1.4 // 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 @@ -16,14 +25,20 @@ require ( github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // 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-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/godbus/dbus/v5 v5.1.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/gopherjs/gopherjs v1.17.2 // 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/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect + github.com/lestrrat-go/strftime v1.1.0 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect + github.com/pkg/errors v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rymdport/portal v0.2.6 // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect @@ -35,5 +50,6 @@ require ( golang.org/x/net v0.25.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.16.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ebee727..c9717fb 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,8 @@ 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-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= github.com/go-text/render v0.1.1-0.20240418202334-dd62631dae9b/go.mod h1:jqEuNMenrmj6QRnkdpeaP0oKGFLDNhDkVKwGjsWWYU4= github.com/go-text/typesetting v0.1.0 h1:vioSaLPYcHwPEPLT7gsjCGDCoYSbljxoHJzMnKwVvHw= @@ -203,6 +205,12 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 h1:Po+wkNdMmN+Zj1tDsJQy7mJlPlwGNQd9JZoPjObagf8= github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49/go.mod h1:YiutDnxPRLk5DLUFj6Rw4pRBBURZY07GFr54NdV9mQg= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +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/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= @@ -216,9 +224,17 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN 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/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= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= +github.com/lestrrat-go/strftime v1.1.0 h1:gMESpZy44/4pXLO/m+sL0yBd1W6LjgjrrD4a68Gapyg= +github.com/lestrrat-go/strftime v1.1.0/go.mod h1:uzeIB52CeUJenCo1syghlugshMysrqUT51HlxphXVeI= 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-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/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -239,6 +255,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb 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/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +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/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= @@ -247,6 +264,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= +github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -258,6 +277,8 @@ github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxr github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -426,6 +447,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/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= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -639,6 +661,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -648,6 +672,13 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= +gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 91c42d5..7ac927c 100644 --- a/main.go +++ b/main.go @@ -1,20 +1,28 @@ 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/version" "work_cation/views" ) +const preferenceCurrentTutorial = "currentTutorial" + func main() { + + global.InitDB() + a := app.NewWithID("io.fyne.workCation") a.Settings().SetTheme(&assets.MyTheme{}) a.SetIcon(assets.LogoDataSR) w := a.NewWindow("主页 " + version.NowVersion) - w.Resize(fyne.NewSize(380, 300)) + w.Resize(fyne.NewSize(800, 550)) w.SetContent(mainView(w)) w.CenterOnScreen() w.SetMaster() @@ -23,11 +31,101 @@ func main() { a.Run() } +// 主界面 func mainView(w fyne.Window) fyne.CanvasObject { - return container.NewBorder(nil, nil, nil, nil, - container.NewVBox( - views.ErlangCardView(w, nil), - views.ErlangCardView(w, nil), - ), + var ( + content = container.NewMax() + a = fyne.CurrentApp() + topWindow = w + //title = widget.NewLabel("Component name") + //intro = widget.NewLabel("An introduction would probably go\nhere, as well as a") ) + + setTutorial := func(t views.Tutorial) { + if fyne.CurrentDevice().IsMobile() { + child := a.NewWindow(t.Title) + topWindow = child + child.SetContent(t.View(topWindow)) + child.Show() + child.SetOnClosed(func() { + topWindow = w + }) + return + } + + //title.SetText(t.Title) + //intro.SetText(t.Intro) + + content.Objects = []fyne.CanvasObject{t.View(w)} + content.Refresh() + } + + tutorial := container.NewBorder(nil, nil, nil, nil, content) + //container.NewVBox(title, widget.NewSeparator(), intro) + + split := container.NewHSplit(makeNav(setTutorial, true), tutorial) + split.Offset = 0.2 + return container.NewBorder(nil, nil, nil, nil, split) +} + +// 左侧菜单 +func makeNav(setTutorial func(tutorial views.Tutorial), loadPrevious bool) fyne.CanvasObject { + a := fyne.CurrentApp() + + tree := &widget.Tree{ + ChildUIDs: func(uid string) []string { + return views.TutorialIndex[uid] + }, + IsBranch: func(uid string) bool { + children, ok := views.TutorialIndex[uid] + + return ok && len(children) > 0 + }, + CreateNode: func(branch bool) fyne.CanvasObject { + return widget.NewLabel("Collection Widgets") + }, + UpdateNode: func(uid string, branch bool, obj fyne.CanvasObject) { + t, ok := views.Tutorials[uid] + if !ok { + fyne.LogError("Missing tutorial panel: "+uid, nil) + return + } + obj.(*widget.Label).SetText(t.Title) + if unsupportedTutorial(t) { + obj.(*widget.Label).TextStyle = fyne.TextStyle{Italic: true} + } else { + obj.(*widget.Label).TextStyle = fyne.TextStyle{} + } + }, + OnSelected: func(uid string) { + if t, ok := views.Tutorials[uid]; ok { + 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) + } + + //themes := container.NewGridWithColumns(2, + // widget.NewButton("Dark", func() { + // a.Settings().SetTheme(theme.DarkTheme()) + // }), + // widget.NewButton("Light", func() { + // a.Settings().SetTheme(theme.LightTheme()) + // }), + //) + + return container.NewBorder(nil, nil, nil, nil, tree) +} + +func unsupportedTutorial(t views.Tutorial) bool { + return !t.SupportWeb && fyne.CurrentDevice().IsBrowser() } diff --git a/models/erlangCards.go b/models/erlangCards.go index e125876..55d441c 100644 --- a/models/erlangCards.go +++ b/models/erlangCards.go @@ -1,17 +1,19 @@ package models -type ErlangCards struct { - UUID string `json:"uuid"` // id ip加时间戳生成 - UserIp string `json:"user_ip"` // 用户ip - Title string `json:"title"` // 标题 - Text string `json:"text"` // 内容 - Covers []string `json:"covers"` // 封面 - Template string `json:"template"` // 模版 - VarName []string `json:"var_name"` // 变量 - VarContent []string `json:"var_content"` // 变量内容 - IsShowOut bool `json:"is_show_out"` // 是否展示 +import "work_cation/pkg/gormx" - Number string `json:"number"` // 数量 - Goods string `json:"goods"` // 点赞 - Collection string `json:"collection"` // 收藏 +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"` // 封面 + 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"` // 收藏 } diff --git a/models/history.go b/models/history.go index beaa512..5ccc557 100644 --- a/models/history.go +++ b/models/history.go @@ -1,6 +1,8 @@ package models +import "work_cation/pkg/gormx" + type History struct { - UUID string `json:"uuid"` - Vars []string `json:"vars"` + UUID string `json:"uuid"` + Vars gormx.ListString `json:"vars"` } diff --git a/models/repo/erlangCard.go b/models/repo/erlangCard.go new file mode 100644 index 0000000..a5770ce --- /dev/null +++ b/models/repo/erlangCard.go @@ -0,0 +1,20 @@ +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 +} diff --git a/models/repo/user.go b/models/repo/user.go new file mode 100644 index 0000000..8a7a1fb --- /dev/null +++ b/models/repo/user.go @@ -0,0 +1,35 @@ +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 +} diff --git a/models/user.go b/models/user.go index edd7c41..ab605f7 100644 --- a/models/user.go +++ b/models/user.go @@ -1,7 +1,7 @@ package models type Users struct { - Ip string `json:"ip"` + Ip string `gorm:"primarykey" json:"ip"` Name string `json:"name"` // 昵称 Avatar string `json:"avatar"` // 头像 Cover string `json:"cover"` // 封面 diff --git a/pkg/gormx/finder.go b/pkg/gormx/finder.go new file mode 100644 index 0000000..4b232f1 --- /dev/null +++ b/pkg/gormx/finder.go @@ -0,0 +1,60 @@ +package gormx + +import ( + "gorm.io/gorm" +) + +type LimitHandler func(db *gorm.DB) *gorm.DB + +func Finder[T any](model T, db *gorm.DB, objs ...LimitHandler) (int64, []T, error) { + for _, obj := range objs { + db = obj(db) + } + var models []T + err := db.Model(&model).Find(&models).Error + if err != nil { + return 0, nil, err + } + var sum int64 + err = db.Model(&model).Count(&sum).Error + return sum, models, err +} + +// FinderPage 分页查找 +func FinderPage[T any](db *gorm.DB, page, limit int, objs ...LimitHandler) (int64, []T, error) { + return pageFinder[T](db, pageHandle(page, limit), objs...) +} + +func pageFinder[T any](db *gorm.DB, limit LimitHandler, objs ...LimitHandler) (int64, []T, error) { + // 定义表 + var model T + db = db.Model(&model) + for _, obj := range objs { + db = obj(db) + } + // 查找全部数量 + var sum int64 + if err := db.Model(&model).Count(&sum).Error; err != nil { + return 0, nil, err + } + // 分页查找 + db = limit(db) + var models []T + if err := db.Find(&models).Error; err != nil { + return 0, nil, err + } + return sum, models, nil +} + +func pageHandle(page, limit int) LimitHandler { + return func(db *gorm.DB) *gorm.DB { + // 用户输入起始位 + var beginIndex int + if page < 0 { + beginIndex = -1 + } else { + beginIndex = (page - 1) * limit + } + return db.Offset(beginIndex).Limit(limit) + } +} diff --git a/pkg/gormx/gormx.go b/pkg/gormx/gormx.go new file mode 100644 index 0000000..df10b12 --- /dev/null +++ b/pkg/gormx/gormx.go @@ -0,0 +1,27 @@ +package gormx + +import ( + "errors" + "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +type Config struct { + Type string `ini:"type"` + DSN string `ini:"dsn"` +} + +func New(cfg Config) (db *gorm.DB, err error) { + switch cfg.Type { + case "sqlite3": + // dsn := "exe.db" + db, err = gorm.Open(sqlite.Open(cfg.DSN), &gorm.Config{}) + case "mysql": + // dsn := "userRepo:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" + db, err = gorm.Open(mysql.Open(cfg.DSN), &gorm.Config{}) + default: + err = errors.New("mode not supported") + } + return +} diff --git a/pkg/gormx/models.go b/pkg/gormx/models.go new file mode 100644 index 0000000..65b0585 --- /dev/null +++ b/pkg/gormx/models.go @@ -0,0 +1,108 @@ +package gormx + +import ( + "database/sql/driver" + "encoding/json" + "errors" + "fmt" +) + +type ListInt64 []int64 + +// Value 接口,Value 返回 json value any -> string +func (j ListInt64) Value() (driver.Value, error) { + return json.Marshal(j) +} + +// Scan 接口,Scan 将 value 扫描至 Jsonb +func (j *ListInt64) 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 +} + +type ListUint []uint + +// Value 接口,Value 返回 json value any -> string +func (j ListUint) Value() (driver.Value, error) { + return json.Marshal(j) +} + +// Scan 接口,Scan 将 value 扫描至 Jsonb +func (j *ListUint) 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 +} + +type ListInt []int + +// Value 接口,Value 返回 json value any -> string +func (j ListInt) Value() (driver.Value, error) { + return json.Marshal(j) +} + +// Scan 接口,Scan 将 value 扫描至 Jsonb +func (j *ListInt) 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 +} + +type ListString []string + +// Value 接口,Value 返回 json value any -> string +func (j ListString) Value() (driver.Value, error) { + return json.Marshal(j) +} + +// Scan 接口,Scan 将 value 扫描至 Jsonb +func (j *ListString) 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 +} + +type MapString map[string]string + +// Value 接口,Value 返回 json value any -> string +func (j MapString) Value() (driver.Value, error) { + return json.Marshal(j) +} + +// Scan 接口,Scan 将 value 扫描至 Jsonb +func (j *MapString) 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 +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..9032687 --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,54 @@ +package logger + +import ( + "io" + "log" + "os" + "path/filepath" + "time" + + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/rifflock/lfshook" + "github.com/sirupsen/logrus" +) + +func Init() { + // 添加日志切割输出 + var hook = NewLfsHook("glogs", time.Hour*12, 6) + logrus.AddHook(hook) + // 忽略控制台打印 + logrus.SetOutput(io.Discard) + // 展示日志行数 + logrus.SetReportCaller(true) +} + +// NewLfsHook 日志钩子(日志拦截,并重定向) +func NewLfsHook(logName string, rotationTime time.Duration, leastDay uint) logrus.Hook { + _ = os.Mkdir(logName, os.ModeDir) + // 可设置按不同level创建不同的文件名,咱们把6中日志都写到同一个writer中 + lfsHook := lfshook.NewHook(lfshook.WriterMap{ + logrus.DebugLevel: NewWriter(filepath.Join(logName, "debug"), rotationTime, leastDay), + logrus.InfoLevel: NewWriter(filepath.Join(logName, "info"), rotationTime, leastDay), + logrus.WarnLevel: NewWriter(filepath.Join(logName, "warn"), rotationTime, leastDay), + logrus.ErrorLevel: NewWriter(filepath.Join(logName, "error"), rotationTime, leastDay), + logrus.FatalLevel: NewWriter(filepath.Join(logName, "fatal"), rotationTime, leastDay), + logrus.PanicLevel: NewWriter(filepath.Join(logName, "panic"), rotationTime, leastDay), + }, nil) //&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"} + return lfsHook +} + +func NewWriter(logName string, rotationTime time.Duration, leastDay uint) io.Writer { + writer, err := rotatelogs.New( + // 1 日志文件名字 + logName+".%Y-%m-%d_%H_%M", // _%S + // 2 日志周期(默认每86400秒/一天旋转一次) + rotatelogs.WithRotationTime(rotationTime), + // 3 清除历史 (WithMaxAge和WithRotationCount只能选其一) + //rotatelogs.WithMaxAge(time.Hour*24*7), //默认每7天清除下日志文件 + rotatelogs.WithRotationCount(leastDay), //只保留最近的N个日志文件 + ) + if err != nil { + log.Panic(err) + } + return writer +} diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go new file mode 100644 index 0000000..0f9cef8 --- /dev/null +++ b/pkg/logger/logger_test.go @@ -0,0 +1,27 @@ +package logger + +import ( + "fmt" + "runtime/debug" + "testing" + "time" + + "github.com/sirupsen/logrus" +) + +func TestLogger(t *testing.T) { + Init() + logrus.Infoln("asdadsasd") + time.Sleep(5 * time.Second) + logrus.Infoln(string(debug.Stack())) + logrus.WithFields(logrus.Fields{"RoleUid": "12313123123"}).Warn("asdasd") +} + +func TestLogger2(t *testing.T) { + str := "goroutine 68 [running]:\\nruntime/debug.Stack()\\n\\tD:/work/environment/GO/src/runtime/debug/stack.go:24 +0x65\\ncolly_v2/pkg/zlib.Try.func1()\\n\\tE:/gopackage2/2023.10/colly_v2/pkg/zlib/try.go:12 " + fmt.Println(len([]byte(str))) +} + +func TestLogger3(t *testing.T) { + fmt.Println(time.Now().Unix() - 259200) +} diff --git a/pkg/utils/hash/hash.go b/pkg/utils/hash/hash.go new file mode 100644 index 0000000..e9249ab --- /dev/null +++ b/pkg/utils/hash/hash.go @@ -0,0 +1,31 @@ +package hash + +import ( + "crypto/md5" + "crypto/sha1" + "fmt" +) + +// MD5 MD5哈希值 +func MD5(b []byte) string { + h := md5.New() + _, _ = h.Write(b) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +// MD5String MD5哈希值 +func MD5String(s string) string { + return MD5([]byte(s)) +} + +// SHA1 SHA1哈希值 +func SHA1(b []byte) string { + h := sha1.New() + _, _ = h.Write(b) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +// SHA1String SHA1哈希值 +func SHA1String(s string) string { + return SHA1([]byte(s)) +} diff --git a/utils/uuid.go b/pkg/utils/uuid.go similarity index 96% rename from utils/uuid.go rename to pkg/utils/uuid.go index b08b866..b724c9e 100644 --- a/utils/uuid.go +++ b/pkg/utils/uuid.go @@ -18,7 +18,6 @@ func CreateUUID() string { // 生成基于字符串的 UUID key := fmt.Sprintln(Get192Ip(), time.Now().Format("2006-01-02_15-04-05"), uuidint) uuidint++ - fmt.Println("key:", key) u1 := uuid.NewSHA1(uuid.Nil, []byte(key)) return u1.String() } diff --git a/views/createErlangCard.go b/views/createErlangCard.go new file mode 100644 index 0000000..65dca49 --- /dev/null +++ b/views/createErlangCard.go @@ -0,0 +1,101 @@ +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) +} diff --git a/views/data.go b/views/data.go new file mode 100644 index 0000000..17c3eaa --- /dev/null +++ b/views/data.go @@ -0,0 +1,29 @@ +package views + +import ( + "fyne.io/fyne/v2" +) + +// Tutorial 定义教程的数据结构 +type Tutorial struct { + Title, Intro string + View func(w fyne.Window) fyne.CanvasObject + SupportWeb bool +} + +var ( + // Tutorials 定义每个教程的元数据 + Tutorials = map[string]Tutorial{ + "welcome": {"主页", "", UserViews, true}, + "canvas": {"我的", "", myCardsViews, true}, + "create": {"新建", "", CreateErlangCard, true}, + } + + // TutorialIndex 定义我们的教程应该如何在索引树中布局 + TutorialIndex = map[string][]string{ + "": {"welcome", "canvas", "create"}, + //"collections": {"list", "table", "tree"}, + //"containers": {"apptabs", "border", "box", "center", "doctabs", "grid", "scroll", "split"}, + //"widgets": {"accordion", "button", "card", "entry", "form", "input", "progress", "text", "toolbar"}, + } +) diff --git a/views/erlangCard.go b/views/erlangCard.go index 22ffa5c..a3f5bb4 100644 --- a/views/erlangCard.go +++ b/views/erlangCard.go @@ -5,13 +5,27 @@ import ( "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" "strings" + "work_cation/global" "work_cation/models" - "work_cation/utils" + "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)) + } + + return container.NewBorder(nil, nil, nil, nil, container.NewScroll( + container.NewGridWrap(fyne.NewSize(200, 200), items...))) +} + func ErlangCardView(_ fyne.Window, data *models.ErlangCards) fyne.CanvasObject { if data == nil { data = &models.ErlangCards{ @@ -32,10 +46,15 @@ func ErlangCardView(_ fyne.Window, data *models.ErlangCards) fyne.CanvasObject { txtWid := widget.NewEntryWithData(txtBound) txtWid.Wrapping = fyne.TextWrapOff - card := widget.NewCard(data.Title, data.Text, widget.NewButton("OPEN", func() { + cardButton := widget.NewButton("OPEN", func() { go UseErlangCard(data) - })) - //card.SetImage(assets.LogoDataSR) + }) + + card := widget.NewCard(data.Title, data.Text, cardButton) + + //image := canvas.NewImageFromResource(assets.LogoDataSR) + //image.FillMode = canvas.ImageFillContain + //card.SetImage(image) return card } @@ -91,7 +110,10 @@ func UseErlangCard(data *models.ErlangCards) { data.VarContent = values //out.SetText(replaceVars(template, data.VarContent)) err := clipboard.WriteAll(replaceVars(template, data.VarContent)) - fmt.Print("复制:", replaceVars(template, data.VarContent), err) + if err != nil { + dialog.ShowError(err, myWindow) + } + upOut() }, } diff --git a/views/userView.go b/views/userView.go new file mode 100644 index 0000000..5b7a9e1 --- /dev/null +++ b/views/userView.go @@ -0,0 +1,32 @@ +package views + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/widget" + "work_cation/assets" + "work_cation/global" + "work_cation/models/repo" +) + +func UserViews(w fyne.Window) fyne.CanvasObject { + image := canvas.NewImageFromResource(assets.LogoDataSR) + 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, + nil, + nil, + nil, + userCard, + ) + +}