first commit

This commit is contained in:
tangtanglove
2023-01-18 13:40:07 +08:00
commit 5244508c40
297 changed files with 24922 additions and 0 deletions

22
.editorconfig Normal file
View File

@@ -0,0 +1,22 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[{*.go,Makefile,.gitmodules,go.mod,go.sum}]
indent_style = tab
[*.md]
indent_style = tab
trim_trailing_whitespace = false
[*.{yml,yaml,json}]
indent_style = space
indent_size = 2
[*.{js,jsx,ts,tsx,css,less,sass,scss,vue,py}]
indent_style = space
indent_size = 4

30
.gitignore vendored Normal file
View File

@@ -0,0 +1,30 @@
# Mac OS X files
.DS_Store
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
# Dependency directories (remove the comment below to include it)
vendor/
# Air tool tmp file
tmp/
# Install lock file
install.lock
# examples web site static files
examples/*/website/

1
LICENSE.md Normal file
View File

@@ -0,0 +1 @@
Hacker license!

1
Makefile Normal file
View File

@@ -0,0 +1 @@
# note: call scripts from /scripts

31
README.md Normal file
View File

@@ -0,0 +1,31 @@
## 介绍
QuarkGO 是一个基于golang的低代码工具它提供的丰富组件能帮助您使用很少的代码就能搭建出功能完善的应用系统。
## 系统特性
**内置功能**
* 管理员管理
* 用户管理
* 权限系统
* 菜单管理
* 系统配置
* 操作日志
* 附件管理
**内置组件**
* Layout组件
* Container组件
* Card组件
* Table组件
* Form组件
* Show组件
* TabForm组件
* ...
## 技术支持
为了避免打扰作者日常工作你可以在Github上提交 [Issues](https://github.com/quarkcms/quark-go/issues)
相关教程,你可以查看 [在线文档](http://www.quarkcms.com/quark-go/)
## License
QuarkGo is licensed under The MIT License (MIT).

0
cmd/quark/.keep Normal file
View File

View File

@@ -0,0 +1,9 @@
# Config file for [Air](https://github.com/cosmtrek/air) in TOML format
# Working directory
# . or absolute path, please note that the directories following must be under root.
root = "."
tmp_dir = "tmp"
[build]
exclude_dir = ["assets", "tmp", "vendor", "website"]

View File

@@ -0,0 +1,35 @@
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/quarkcms/quark-go/pkg/adapter/fiberadapter"
"github.com/quarkcms/quark-go/pkg/app/handler/admin"
"github.com/quarkcms/quark-go/pkg/builder"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
app := fiber.New()
// 数据库配置信息
dsn := "root:Bc5HQFJc4bLjZCcC@tcp(127.0.0.1:3306)/quarkgo?charset=utf8&parseTime=True&loc=Local"
// 配置资源
config := &builder.Config{
AppKey: "123456",
Providers: admin.Providers,
DBConfig: &builder.DBConfig{
Dialector: mysql.Open(dsn),
Opts: &gorm.Config{},
},
}
// 创建对象
b := builder.New(config)
// 适配fiber
fiberadapter.Adapter(b, app)
app.Listen(":3000")
}

View File

@@ -0,0 +1,9 @@
# Config file for [Air](https://github.com/cosmtrek/air) in TOML format
# Working directory
# . or absolute path, please note that the directories following must be under root.
root = "."
tmp_dir = "tmp"
[build]
exclude_dir = ["assets", "tmp", "vendor", "website"]

View File

@@ -0,0 +1,9 @@
# Config file for [Air](https://github.com/cosmtrek/air) in TOML format
# Working directory
# . or absolute path, please note that the directories following must be under root.
root = "."
tmp_dir = "tmp"
[build]
exclude_dir = ["assets", "tmp", "vendor", "website"]

37
examples/hertzadmin/.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
*.o
*.a
*.so
_obj
_test
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.exe~
*.test
*.prof
*.rar
*.zip
*.gz
*.psd
*.bmd
*.cfg
*.pptx
*.log
*nohup.out
*settings.pyc
*.sublime-project
*.sublime-workspace
!.gitkeep
.DS_Store
/.idea
/.vscode
/output
*.local.yml
dumped_hertz_remote_config.json

3
examples/hertzadmin/.hz Normal file
View File

@@ -0,0 +1,3 @@
// Code generated by hz. DO NOT EDIT.
hz version: v0.4.0

View File

@@ -0,0 +1,10 @@
package handler
import (
"github.com/quarkcms/quark-go/examples/hertzadmin/biz/handler/resources"
)
// 注册服务
var Providers = []interface{}{
&resources.Demo{},
}

View File

@@ -0,0 +1,37 @@
package resources
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
)
type Demo struct {
adminresource.Template
}
// 初始化
func (p *Demo) Init() interface{} {
// 初始化模板
p.TemplateInit()
return p
}
// 字段
func (p *Demo) Fields(request *builder.Request) []interface{} {
return []interface{}{}
}
// 搜索
func (p *Demo) Searches(request *builder.Request) []interface{} {
return []interface{}{}
}
// 行为
func (p *Demo) Actions(request *builder.Request) []interface{} {
return []interface{}{}
}

View File

@@ -0,0 +1,12 @@
// Code generated by hertz generator. DO NOT EDIT.
package router
import (
"github.com/cloudwego/hertz/pkg/app/server"
)
// GeneratedRegister registers routers generated by IDL.
func GeneratedRegister(r *server.Hertz) {
//INSERT_POINT: DO NOT DELETE THIS LINE!
}

View File

@@ -0,0 +1,22 @@
// Code generated by hertz generator.
package main
import (
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
h := server.Default(server.WithHostPorts(":3000"))
// 静态文件目录
fs := &app.FS{Root: "/website", IndexNames: []string{"index.html"}}
h.StaticFS("/", fs)
// 注册路由
register(h)
// 启动服务
h.Spin()
}

View File

@@ -0,0 +1,44 @@
// Code generated by hertz generator.
package main
import (
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/quarkcms/quark-go/examples/hertzadmin/biz/handler"
"github.com/quarkcms/quark-go/pkg/adapter/hertzadapter"
"github.com/quarkcms/quark-go/pkg/app/handler/admin"
"github.com/quarkcms/quark-go/pkg/app/install"
"github.com/quarkcms/quark-go/pkg/app/middleware"
"github.com/quarkcms/quark-go/pkg/builder"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// customizeRegister registers customize routers.
func customizedRegister(r *server.Hertz) {
// 数据库配置信息
dsn := "root:Bc5HQFJc4bLjZCcC@tcp(127.0.0.1:3306)/quarkgo?charset=utf8&parseTime=True&loc=Local"
// 配置资源
config := &builder.Config{
AppKey: "123456",
Providers: append(admin.Providers, handler.Providers...),
DBConfig: &builder.DBConfig{
Dialector: mysql.Open(dsn),
Opts: &gorm.Config{},
},
}
// 创建对象
b := builder.New(config)
// 初始化安装
b.Use(install.Handle)
// 中间件
b.Use(middleware.Handle)
// 适配hertz
hertzadapter.Adapter(b, r)
}

View File

@@ -0,0 +1,16 @@
// Code generated by hertz generator. DO NOT EDIT.
package main
import (
"github.com/cloudwego/hertz/pkg/app/server"
router "github.com/quarkcms/quark-go/examples/hertzadmin/biz/router"
)
// register registers all routers.
func register(r *server.Hertz) {
router.GeneratedRegister(r)
customizedRegister(r)
}

93
go.mod Normal file
View File

@@ -0,0 +1,93 @@
module github.com/quarkcms/quark-go
go 1.18
require (
github.com/alibabacloud-go/darabonba-openapi v0.2.1
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18
github.com/alibabacloud-go/tea v1.1.20
github.com/alibabacloud-go/tea-utils v1.4.5
github.com/go-basic/uuid v1.0.0
github.com/gofiber/fiber/v2 v2.41.0
github.com/parnurzeal/gorequest v0.2.16
)
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/bytedance/go-tagexpr/v2 v2.9.2 // indirect
github.com/bytedance/gopkg v0.0.0-20220531084716-665b4f21126f // indirect
github.com/bytedance/sonic v1.5.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 // indirect
github.com/cloudwego/netpoll v0.3.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/henrylee2cn/ameda v1.4.10 // indirect
github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/nyaruka/phonenumbers v1.0.55 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.3 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/tidwall/gjson v1.13.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.43.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/arch v0.0.0-20220722155209-00200b7164a7 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
github.com/alibabacloud-go/openapi-util v0.0.11 // indirect
github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
github.com/aliyun/credentials-go v1.1.2 // indirect
github.com/clbanning/mxj/v2 v2.5.6 // indirect
github.com/cloudwego/hertz v0.4.2
github.com/dchest/captcha v1.0.0
github.com/derekstavis/go-qs v0.0.0-20180720192143-9eef69e6c4e7
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 // indirect
github.com/go-redis/redis/v8 v8.11.5
github.com/gobeam/stringy v0.0.5
github.com/golang-jwt/jwt/v4 v4.4.3
github.com/jinzhu/copier v0.3.5
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/xuri/excelize/v2 v2.6.1
golang.org/x/crypto v0.5.0
golang.org/x/net v0.5.0 // indirect
gopkg.in/ini.v1 v1.56.0 // indirect
gorm.io/driver/mysql v1.4.5
gorm.io/gorm v1.24.3
moul.io/http2curl v1.0.0 // indirect
)
replace github.com/apache/thrift => github.com/apache/thrift v0.13.0

279
go.sum Normal file
View File

@@ -0,0 +1,279 @@
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc=
github.com/alibabacloud-go/darabonba-openapi v0.2.1 h1:WyzxxKvhdVDlwpAMOHgAiCJ+NXa6g5ZWPFEzaK/ewwY=
github.com/alibabacloud-go/darabonba-openapi v0.2.1/go.mod h1:zXOqLbpIqq543oioL9IuuZYOQgHQ5B8/n5OPrnko8aY=
github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 h1:NqugFkGxx1TXSh/pBcU00Y6bljgDPaFdh5MUSeJ7e50=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18 h1:hfZA4cgIl6frNdsRmAyj8sn9J1bihQpYbzIVv2T/+Cs=
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18/go.mod h1:di54xjBFHvKiQQo7st3TUmiMy0ywne5TOHup786Rhes=
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/openapi-util v0.0.11 h1:iYnqOPR5hyEEnNZmebGyRMkkEJRWUEjDiiaOHZ5aNhA=
github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.1.20 h1:wFK4xEbvGYMtzTyHhIju9D7ecWxvSUdoLO6y4vDLFik=
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA=
github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M=
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I=
github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM=
github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q=
github.com/bytedance/gopkg v0.0.0-20220531084716-665b4f21126f h1:2YCF3cgO6XCub0HIsLrA8ZGhmAPGZfOeSaGjT6Kx4Mc=
github.com/bytedance/gopkg v0.0.0-20220531084716-665b4f21126f/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q=
github.com/bytedance/sonic v1.5.0 h1:XWdTi8bwPgxIML+eNV1IwNuTROK6EUrQ65ey8yd6fRQ=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/clbanning/mxj/v2 v2.5.6 h1:Jm4VaCI/+Ug5Q57IzEoZbwx4iQFA6wkXv72juUSeK+g=
github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/cloudwego/hertz v0.4.2 h1:Ntfs5MdPoKeFSbyStU2drM4CizOkEfYWsB9s1Q3taPY=
github.com/cloudwego/hertz v0.4.2/go.mod h1:K1U0RlU07CDeBINfHNbafH/3j9uSgIW8otbjUys3OPY=
github.com/cloudwego/netpoll v0.3.1 h1:xByoORmCLIyKZ8gS+da06WDo3j+jvmhaqS2KeKejtBk=
github.com/cloudwego/netpoll v0.3.1/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/captcha v1.0.0 h1:vw+bm/qMFvTgcjQlYVTuQBJkarm5R0YSsDKhm1HZI2o=
github.com/dchest/captcha v1.0.0/go.mod h1:7zoElIawLp7GUMLcj54K9kbw+jEyvz2K0FDdRRYhvWo=
github.com/derekstavis/go-qs v0.0.0-20180720192143-9eef69e6c4e7 h1:zmAiXR9h1TCVN/0yCMRYQNE91dNRORpSzMFiqfTTPOs=
github.com/derekstavis/go-qs v0.0.0-20180720192143-9eef69e6c4e7/go.mod h1:Vgz4nKcG6+B7QcALsWZpmhyQTLSl7nwFGKSrbq2LxEo=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/go-basic/uuid v1.0.0 h1:Faqtetcr8uwOzR2qp8RSpkahQiv4+BnJhrpuXPOo63M=
github.com/go-basic/uuid v1.0.0/go.mod h1:yVtVnsXcmaLc9F4Zw7hTV7R0+vtuQw00mdXi+F6tqco=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
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/gobeam/stringy v0.0.5 h1:TvxQGSAqr/qF0SBVxa8Q67WWIo7bCWS0bM101WOd52g=
github.com/gobeam/stringy v0.0.5/go.mod h1:W3620X9dJHf2FSZF5fRnWekHcHQjwmCz8ZQ2d1qloqE=
github.com/gofiber/fiber/v2 v2.41.0 h1:YhNoUS/OTjEz+/WLYuQ01xI7RXgKEFnGBKMagAu5f0M=
github.com/gofiber/fiber/v2 v2.41.0/go.mod h1:RdebcCuCRFp4W6hr3968/XxwJVg0K+jr9/Ae0PFzZ0Q=
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4=
github.com/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk=
github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4=
github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 h1:yE9ULgp02BhYIrO6sdV/FPe0xQM6fNHkVQW2IAymfM0=
github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
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.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.10/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/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg=
github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M=
github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g=
github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.6.1 h1:ICBdtw803rmhLN3zfvyEGH3cwSmZv+kde7LhTDT659k=
github.com/xuri/excelize/v2 v2.6.1/go.mod h1:tL+0m6DNwSXj/sILHbQTYsLi9IF4TW59H2EF3Yrx1AU=
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M=
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.0.0-20220722155209-00200b7164a7 h1:VBQqJMNMRfQsWSiCTLgz9XjAfWlgnJAPv8nsp1HF8Tw=
golang.org/x/arch v0.0.0-20220722155209-00200b7164a7/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE=
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/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.4.5 h1:u1lytId4+o9dDaNcPCFzNv7h6wvmc92UjNk3z8enSBU=
gorm.io/driver/mysql v1.4.5/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.3 h1:WL2ifUmzR/SLp85CSURAfybcHnGZ+yLSGSxgYXlFBHg=
gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@@ -0,0 +1,106 @@
package aliyunsms
import (
"regexp"
openapi "github.com/alibabacloud-go/darabonba-openapi/client"
dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v2/client"
util "github.com/alibabacloud-go/tea-utils/service"
"github.com/alibabacloud-go/tea/tea"
)
// 配置
type Config struct {
AccessKeyId string
AccessKeySecret string
SignName string
TemplateCode string
}
// 结构体
type App struct {
Config *Config
}
// 初始化
func New(config *Config) *App {
return &App{
Config: config,
}
}
// 发送短信
func (p *App) SendSms(phone string, code string) (bool, string) {
// 匹配规则
regRuler := "^1[345789]{1}\\d{9}$"
// 正则调用规则
reg := regexp.MustCompile(regRuler)
// 返回 MatchString 是否匹配
if !reg.MatchString(phone) {
return false, "手机号格式错误!"
}
client, _err := Client(tea.String(p.Config.AccessKeyId), tea.String(p.Config.AccessKeySecret))
if _err != nil {
return false, _err.Error()
}
sendSmsRequest := &dysmsapi20170525.SendSmsRequest{
PhoneNumbers: tea.String(phone),
SignName: tea.String(p.Config.SignName),
TemplateCode: tea.String(p.Config.TemplateCode),
TemplateParam: tea.String("{\"code\":\"" + code + "\"}"),
}
runtime := &util.RuntimeOptions{}
tryErr := func() (_e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
// 复制代码运行请自行打印 API 的返回值
_, _err = client.SendSmsWithOptions(sendSmsRequest, runtime)
if _err != nil {
return _err
}
return nil
}()
if tryErr != nil {
var error = &tea.SDKError{}
if _t, ok := tryErr.(*tea.SDKError); ok {
error = _t
} else {
error.Message = tea.String(tryErr.Error())
}
// 如有需要,请打印 error
util.AssertAsString(error.Message)
}
if _err != nil {
return false, _err.Error()
}
return true, ""
}
// 使用AK&SK初始化账号Client
func Client(accessKeyId *string, accessKeySecret *string) (_result *dysmsapi20170525.Client, _err error) {
config := &openapi.Config{
// 您的 AccessKey ID
AccessKeyId: accessKeyId,
// 您的 AccessKey Secret
AccessKeySecret: accessKeySecret,
}
// 访问的域名
config.Endpoint = tea.String("dysmsapi.aliyuncs.com")
_result = &dysmsapi20170525.Client{}
_result, _err = dysmsapi20170525.NewClient(config)
return _result, _err
}

View File

@@ -0,0 +1,74 @@
package aliyunsms
import (
"crypto/md5"
"encoding/json"
"fmt"
"regexp"
"github.com/parnurzeal/gorequest"
)
// 配置
type Config struct {
Uid string
Password string
}
// 结构体
type App struct {
Config *Config
}
// 初始化
func New(config *Config) *App {
return &App{
Config: config,
}
}
// 发送短信
func (p *App) SendSms(phone string, content string) (bool, string) {
// 匹配规则
regRuler := "^1[345789]{1}\\d{9}$"
// 正则调用规则
reg := regexp.MustCompile(regRuler)
// 返回 MatchString 是否匹配
if !reg.MatchString(phone) {
return false, "手机号格式错误!"
}
uid := p.Config.Uid
password := p.Config.Password
if uid == "" || password == "" {
return false, "接口配置错误!"
}
md5Byte := md5.Sum([]byte(password))
md5Password := fmt.Sprintf("%x", md5Byte)
// 接口url
url := "https://submit.10690221.com/send/ordinarykv?uid=" + uid + "&password=" + md5Password + "&mobile=" + phone + "&msg=" + content
request := gorequest.New()
_, body, _ := request.Get(url).End()
type Data struct {
Msg string
Code int
MsgId string
}
var data Data
json.Unmarshal([]byte(body), &data)
if data.Code == 0 {
return true, "发送成功"
} else {
return true, data.Msg
}
}

View File

@@ -0,0 +1,94 @@
package fiberadapter
import (
"bytes"
"github.com/gofiber/fiber/v2"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/msg"
)
const COMPONENT_RESPONSE = "component" // 组件类型响应
const ACTION_RESPONSE = "action" // 行为类型响应
const FILE_RESPONSE = "file" // 文件类型响应
// 将gofiber框架的Ctx转换为builder框架Request
func RequestAdapter(ctx *fiber.Ctx) (*builder.Request, error) {
return &builder.Request{
IPString: ctx.IP(),
HeaderString: ctx.Request().Header.String(),
MethodString: string(ctx.Method()),
FullPathString: ctx.Route().Path,
HostString: string(ctx.Hostname()),
PathString: string(ctx.Path()),
QueryString: string(ctx.Context().QueryArgs().QueryString()),
BodyBuffer: ctx.Body(),
}, nil
}
// 适配gofiber框架响应
func ResponseAdapter(r *builder.Resource, responseType string, ctx *fiber.Ctx) error {
var responseError error
result, err := r.Run()
if err != nil {
return ctx.JSON(msg.Error(err.Error(), ""))
}
switch responseType {
case "component":
return ctx.JSON(result)
case "action":
return ctx.JSON(msg.Success("操作成功", "", result))
case "file":
return ctx.SendStream(bytes.NewReader(result.([]byte)))
}
return responseError
}
// 适配gofiber框架路由
func RouteAdapter(b *builder.Resource, responseType string, ctx *fiber.Ctx) error {
// 适配请求
request, err := RequestAdapter(ctx)
if err != nil {
return ctx.JSON(msg.Error(err.Error(), ""))
}
// 适配响应
resource := b.TransformRequest(request)
return ResponseAdapter(resource, responseType, ctx)
}
// 适配gofiber框架
func Adapter(b *builder.Resource, app *fiber.App) {
// 后台路由组
rg := app.Group("/api/admin")
// 登录
rg.Get("/login/:resource/index", func(ctx *fiber.Ctx) error {
return RouteAdapter(b, COMPONENT_RESPONSE, ctx)
})
rg.Post("/login/:resource/handle", func(ctx *fiber.Ctx) error {
return RouteAdapter(b, ACTION_RESPONSE, ctx)
})
rg.Get("/login/:resource/captchaId", func(ctx *fiber.Ctx) error {
return RouteAdapter(b, ACTION_RESPONSE, ctx)
})
rg.Get("/login/:resource/captcha/:id", func(ctx *fiber.Ctx) error {
return RouteAdapter(b, FILE_RESPONSE, ctx)
})
// 仪表盘
rg.Get("/dashboard/:resource/index", func(ctx *fiber.Ctx) error {
return RouteAdapter(b, COMPONENT_RESPONSE, ctx)
})
// 增删改查
rg.Get("/:resource/index", func(ctx *fiber.Ctx) error {
return RouteAdapter(b, COMPONENT_RESPONSE, ctx)
})
}

View File

@@ -0,0 +1,160 @@
package hertzadapter
import (
"context"
"time"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/msg"
)
const JSON_RESPONSE = "json" // json类型响应
const IMAGE_RESPONSE = "image" // 图片类型响应
const EXCEL_RESPONSE = "excel" // Excel文件类型响应
// 将hertz框架的RequestContext转发到builder的Request
func RequestAdapter(ctx *app.RequestContext) (*builder.Request, error) {
body, err := ctx.Body()
if err != nil {
return nil, err
}
return &builder.Request{
IPString: ctx.ClientIP(),
HeaderString: string(ctx.Request.Header.Header()),
MethodString: string(ctx.Method()),
FullPathString: ctx.FullPath(),
HostString: string(ctx.Host()),
PathString: string(ctx.Path()),
QueryString: string(ctx.Request.QueryString()),
BodyBuffer: body,
}, nil
}
// 适配hertz框架响应
func ResponseAdapter(r *builder.Resource, responseType string, ctx *app.RequestContext) {
result, err := r.Run()
if err != nil {
ctx.JSON(200, msg.Error(err.Error(), ""))
return
}
switch responseType {
case JSON_RESPONSE:
ctx.JSON(200, result)
case IMAGE_RESPONSE:
ctx.Write(result.([]byte))
}
}
// 适配hertz框架路由
func RouteAdapter(b *builder.Resource, responseType string, ctx *app.RequestContext) {
body, err := ctx.Body()
if err != nil {
ctx.JSON(200, msg.Error(err.Error(), ""))
return
}
// 将hertz框架请求转换为builder框架请求
request := &builder.Request{
IPString: ctx.ClientIP(),
HeaderString: string(ctx.Request.Header.Header()),
MethodString: string(ctx.Method()),
FullPathString: ctx.FullPath(),
HostString: string(ctx.Host()),
PathString: string(ctx.Path()),
QueryString: string(ctx.Request.QueryString()),
BodyBuffer: body,
}
// 转换Request对象
result, err := b.TransformRequest(request).Run()
if err != nil {
ctx.JSON(200, msg.Error(err.Error(), ""))
return
}
// 响应结果
switch responseType {
case JSON_RESPONSE:
ctx.JSON(200, result)
case IMAGE_RESPONSE:
ctx.Write(result.([]byte))
case EXCEL_RESPONSE:
ctx.Response.Header.Set("Content-Disposition", "attachment; filename=data_"+time.Now().Format("20060102150405")+".xlsx")
ctx.Response.Header.Set("Content-Type", "application/octet-stream")
ctx.Write(result.([]byte))
}
}
// 适配hertz框架
func Adapter(b *builder.Resource, r *server.Hertz) {
// 后台路由组
rg := r.Group("/api/admin")
// 登录
rg.GET("/login/:resource/index", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.POST("/login/:resource/handle", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/login/:resource/captchaId", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/login/:resource/captcha/:id", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, IMAGE_RESPONSE, ctx)
})
rg.GET("/logout/:resource/handle", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
// 仪表盘
rg.GET("/dashboard/:resource/index", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
// 增删改查
rg.GET("/:resource/index", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/:resource/editable", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.Any("/:resource/action/:uriKey", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/:resource/create", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.POST("/:resource/store", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/:resource/edit", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/:resource/edit/values", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.POST("/:resource/save", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/:resource/detail", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/:resource/export", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, EXCEL_RESPONSE, ctx)
})
rg.Any("/:resource/import", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
rg.GET("/:resource/import/template", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, EXCEL_RESPONSE, ctx)
})
rg.GET("/:resource/:uriKey/form", func(c context.Context, ctx *app.RequestContext) {
RouteAdapter(b, JSON_RESPONSE, ctx)
})
}

View File

@@ -0,0 +1,45 @@
package actions
import (
"encoding/json"
models "github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/hash"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type ChangeAccount struct {
actions.Action
}
// 执行行为句柄
func (p *ChangeAccount) Handle(request *builder.Request, model *gorm.DB) interface{} {
data := map[string]interface{}{}
json.Unmarshal(request.Body(), &data)
if data["avatar"] != "" {
data["avatar"], _ = json.Marshal(data["avatar"])
} else {
data["avatar"] = nil
}
// 加密密码
if data["password"] != nil {
data["password"] = hash.Make(data["password"].(string))
}
// 获取登录管理员信息
adminInfo, err := (&models.Admin{}).GetAuthUser(request.Token())
if err != nil {
return msg.Error(err.Error(), "")
}
err = model.Where("id", adminInfo.Id).Updates(data).Error
if err != nil {
return msg.Error(err.Error(), "")
}
return msg.Success("操作成功", "", "")
}

View File

@@ -0,0 +1,72 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type ChangeStatus struct {
actions.Action
}
// 初始化
func (p *ChangeStatus) Init() *ChangeStatus {
// 初始化父结构
p.ParentInit()
// 行为名称当行为在表格行展示时支持js表达式
p.Name = "<%= (status==1 ? '禁用' : '启用') %>"
// 设置按钮类型,primary | ghost | dashed | link | text | default
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 执行成功后刷新的组件
p.Reload = "table"
// 设置展示位置
p.SetOnlyOnIndexTableRow(true)
// 当行为在表格行展示时支持js表达式
p.WithConfirm("确定要<%= (status==1 ? '禁用' : '启用') %>数据吗?", "", "pop")
return p
}
/**
* 行为接口接收的参数,当行为在表格行展示的时候,可以配置当前行的任意字段
*
* @return array
*/
func (p *ChangeStatus) GetApiParams() []string {
return []string{
"id",
"status",
}
}
// 执行行为句柄
func (p *ChangeStatus) Handle(request *builder.Request, model *gorm.DB) interface{} {
status := request.Query("status")
if status == "" {
return msg.Error("参数错误!", "")
}
var fieldStatus int
if status == "1" {
fieldStatus = 0
} else {
fieldStatus = 1
}
err := model.Update("status", fieldStatus).Error
if err != nil {
return msg.Error(err.Error(), "")
}
return msg.Success("操作成功", "", "")
}

View File

@@ -0,0 +1,51 @@
package actions
import (
"encoding/json"
models "github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/dal/db"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type ChangeWebConfig struct {
actions.Action
}
// 执行行为句柄
func (p *ChangeWebConfig) Handle(request *builder.Request, model *gorm.DB) interface{} {
data := map[string]interface{}{}
json.Unmarshal(request.Body(), &data)
result := true
for k, v := range data {
config := map[string]interface{}{}
db.Client.Model(&models.Config{}).Where("name =?", k).First(&config)
if getValue, ok := v.([]interface{}); ok {
v, _ = json.Marshal(getValue)
}
if getValue, ok := v.([]map[string]interface{}); ok {
v, _ = json.Marshal(getValue)
}
if getValue, ok := v.(map[string]interface{}); ok {
v, _ = json.Marshal(getValue)
}
updateResult := db.Client.Model(&models.Config{}).Where("name", k).Update("value", v)
if updateResult.Error != nil {
result = false
}
}
if !result {
return msg.Error("操作失败,请重试!", "")
}
// 刷新网站配置
(&models.Config{}).Refresh()
// 返回成功
return msg.Success("操作成功", "", "")
}

View File

@@ -0,0 +1,88 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/component/admin/action"
"github.com/quarkcms/quark-go/pkg/component/admin/form"
)
type CreateDrawer struct {
actions.Drawer
}
// 初始化
func (p *CreateDrawer) Init(name string) *CreateDrawer {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "primary"
// 图标
p.Icon = "plus-circle"
// 文字
p.Name = "创建" + name
// 执行成功后刷新的组件
p.Reload = "table"
// 关闭时销毁 Drawer 里的子元素
p.DestroyOnClose = true
// 设置展示位置
p.SetOnlyOnIndex(true)
return p
}
// 内容
func (p *CreateDrawer) GetBody(request *builder.Request, resourceInstance interface{}) interface{} {
api := resourceInstance.(interface {
CreationApi(*builder.Request, interface{}) string
}).CreationApi(request, resourceInstance)
fields := resourceInstance.(interface {
CreationFieldsWithinComponents(*builder.Request, interface{}) interface{}
}).CreationFieldsWithinComponents(request, resourceInstance)
// 断言BeforeCreating方法获取初始数据
data := resourceInstance.(interface {
BeforeCreating(*builder.Request) map[string]interface{}
}).BeforeCreating(request)
return (&form.Component{}).
Init().
SetKey("createDrawerForm", false).
SetApi(api).
SetBody(fields).
SetInitialValues(data).
SetLabelCol(map[string]interface{}{
"span": 6,
}).
SetWrapperCol(map[string]interface{}{
"span": 18,
})
}
// 弹窗行为
func (p *CreateDrawer) GetActions(request *builder.Request, resourceInstance interface{}) []interface{} {
return []interface{}{
(&action.Component{}).
Init().
SetLabel("取消").
SetActionType("cancel"),
(&action.Component{}).
Init().
SetLabel("提交").
SetWithLoading(true).
SetReload("table").
SetActionType("submit").
SetType("primary", false).
SetSubmitForm("createDrawerForm"),
}
}

View File

@@ -0,0 +1,37 @@
package actions
import (
"strings"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
)
type CreateLink struct {
actions.Link
}
// 初始化
func (p *CreateLink) Init(name string) *CreateLink {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "primary"
// 图标
p.Icon = "plus-circle"
// 文字
p.Name = "创建" + name
// 设置展示位置
p.SetOnlyOnIndex(true)
return p
}
// 跳转链接
func (p *CreateLink) GetHref(request *builder.Request) string {
return "#/index?api=" + strings.Replace(request.Path(), "/index", "/create", -1)
}

View File

@@ -0,0 +1,88 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/component/admin/action"
"github.com/quarkcms/quark-go/pkg/component/admin/form"
)
type CreateModal struct {
actions.Modal
}
// 初始化
func (p *CreateModal) Init(name string) *CreateModal {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "primary"
// 图标
p.Icon = "plus-circle"
// 文字
p.Name = "创建" + name
// 关闭时销毁 Modal 里的子元素
p.DestroyOnClose = true
// 执行成功后刷新的组件
p.Reload = "table"
// 设置展示位置
p.SetOnlyOnIndex(true)
return p
}
// 内容
func (p *CreateModal) GetBody(request *builder.Request, resourceInstance interface{}) interface{} {
api := resourceInstance.(interface {
CreationApi(*builder.Request, interface{}) string
}).CreationApi(request, resourceInstance)
fields := resourceInstance.(interface {
CreationFieldsWithinComponents(*builder.Request, interface{}) interface{}
}).CreationFieldsWithinComponents(request, resourceInstance)
// 断言BeforeCreating方法获取初始数据
data := resourceInstance.(interface {
BeforeCreating(*builder.Request) map[string]interface{}
}).BeforeCreating(request)
return (&form.Component{}).
Init().
SetKey("createModalForm", false).
SetApi(api).
SetBody(fields).
SetInitialValues(data).
SetLabelCol(map[string]interface{}{
"span": 6,
}).
SetWrapperCol(map[string]interface{}{
"span": 18,
})
}
// 弹窗行为
func (p *CreateModal) GetActions(request *builder.Request, resourceInstance interface{}) []interface{} {
return []interface{}{
(&action.Component{}).
Init().
SetLabel("取消").
SetActionType("cancel"),
(&action.Component{}).
Init().
SetLabel("提交").
SetWithLoading(true).
SetReload("table").
SetActionType("submit").
SetType("primary", false).
SetSubmitForm("createModalForm"),
}
}

View File

@@ -0,0 +1,64 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type Delete struct {
actions.Action
}
// 初始化
func (p *Delete) Init(name string) *Delete {
// 初始化父结构
p.ParentInit()
// 行为名称当行为在表格行展示时支持js表达式
p.Name = name
// 设置按钮类型,primary | ghost | dashed | link | text | default
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 执行成功后刷新的组件
p.Reload = "table"
// 当行为在表格行展示时支持js表达式
p.WithConfirm("确定要删除吗?", "删除后数据将无法恢复,请谨慎操作!", "modal")
if name == "删除" {
p.SetOnlyOnIndexTableRow(true)
}
if name == "批量删除" {
p.SetOnlyOnIndexTableAlert(true)
}
return p
}
/**
* 行为接口接收的参数,当行为在表格行展示的时候,可以配置当前行的任意字段
*
* @return array
*/
func (p *Delete) GetApiParams() []string {
return []string{
"id",
}
}
// 执行行为句柄
func (p *Delete) Handle(request *builder.Request, model *gorm.DB) interface{} {
err := model.Delete("").Error
if err != nil {
return msg.Error(err.Error(), "")
}
return msg.Success("操作成功", "", "")
}

View File

@@ -0,0 +1,37 @@
package actions
import (
"strings"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
)
type DetailLink struct {
actions.Link
}
// 初始化
func (p *DetailLink) Init(name string) *DetailLink {
// 初始化父结构
p.ParentInit()
// 设置按钮类型,primary | ghost | dashed | link | text | default
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 文字
p.Name = name
// 设置展示位置
p.SetOnlyOnIndexTableRow(true)
return p
}
// 跳转链接
func (p *DetailLink) GetHref(request *builder.Request) string {
return "#/index?api=" + strings.Replace(request.Path(), "/index", "/detail&id=${id}", -1)
}

View File

@@ -0,0 +1,59 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type Disable struct {
actions.Action
}
// 初始化
func (p *Disable) Init(name string) *Disable {
// 初始化父结构
p.ParentInit()
// 行为名称当行为在表格行展示时支持js表达式
p.Name = name
// 设置按钮类型,primary | ghost | dashed | link | text | default
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 执行成功后刷新的组件
p.Reload = "table"
// 设置展示位置
p.SetOnlyOnIndexTableAlert(true)
// 当行为在表格行展示时支持js表达式
p.WithConfirm("确定要禁用吗?", "禁用后数据将无法使用,请谨慎操作!", "modal")
return p
}
/**
* 行为接口接收的参数,当行为在表格行展示的时候,可以配置当前行的任意字段
*
* @return array
*/
func (p *Disable) GetApiParams() []string {
return []string{
"id",
}
}
// 执行行为句柄
func (p *Disable) Handle(request *builder.Request, model *gorm.DB) interface{} {
err := model.Update("status", 0).Error
if err != nil {
return msg.Error(err.Error(), "")
}
return msg.Success("操作成功", "", "")
}

View File

@@ -0,0 +1,87 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/component/admin/action"
"github.com/quarkcms/quark-go/pkg/component/admin/form"
)
type EditDrawer struct {
actions.Drawer
}
// 初始化
func (p *EditDrawer) Init(name string) *EditDrawer {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 文字
p.Name = name
// 关闭时销毁 Drawer 里的子元素
p.DestroyOnClose = true
// 执行成功后刷新的组件
p.Reload = "table"
// 设置展示位置
p.SetOnlyOnIndexTableRow(true)
return p
}
// 内容
func (p *EditDrawer) GetBody(request *builder.Request, resourceInstance interface{}) interface{} {
api := resourceInstance.(interface {
UpdateApi(*builder.Request, interface{}) string
}).UpdateApi(request, resourceInstance)
initApi := resourceInstance.(interface {
EditValueApi(*builder.Request) string
}).EditValueApi(request)
fields := resourceInstance.(interface {
UpdateFieldsWithinComponents(*builder.Request, interface{}) interface{}
}).UpdateFieldsWithinComponents(request, resourceInstance)
return (&form.Component{}).
Init().
SetKey("editDrawerForm", false).
SetApi(api).
SetInitApi(initApi).
SetBody(fields).
SetLabelCol(map[string]interface{}{
"span": 6,
}).
SetWrapperCol(map[string]interface{}{
"span": 18,
})
}
// 弹窗行为
func (p *EditDrawer) GetActions(request *builder.Request, resourceInstance interface{}) []interface{} {
return []interface{}{
(&action.Component{}).
Init().
SetLabel("取消").
SetActionType("cancel"),
(&action.Component{}).
Init().
SetLabel("提交").
SetWithLoading(true).
SetReload("table").
SetActionType("submit").
SetType("primary", false).
SetSubmitForm("editDrawerForm"),
}
}

View File

@@ -0,0 +1,37 @@
package actions
import (
"strings"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
)
type EditLink struct {
actions.Link
}
// 初始化
func (p *EditLink) Init(name string) *EditLink {
// 初始化父结构
p.ParentInit()
// 设置按钮类型,primary | ghost | dashed | link | text | default
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 文字
p.Name = name
// 设置展示位置
p.SetOnlyOnIndexTableRow(true)
return p
}
// 跳转链接
func (p *EditLink) GetHref(request *builder.Request) string {
return "#/index?api=" + strings.Replace(request.Path(), "/index", "/edit&id=${id}", -1)
}

View File

@@ -0,0 +1,87 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/component/admin/action"
"github.com/quarkcms/quark-go/pkg/component/admin/form"
)
type EditModal struct {
actions.Modal
}
// 初始化
func (p *EditModal) Init(name string) *EditModal {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 文字
p.Name = name
// 关闭时销毁 Modal 里的子元素
p.DestroyOnClose = true
// 执行成功后刷新的组件
p.Reload = "table"
// 设置展示位置
p.SetOnlyOnIndexTableRow(true)
return p
}
// 内容
func (p *EditModal) GetBody(request *builder.Request, resourceInstance interface{}) interface{} {
api := resourceInstance.(interface {
UpdateApi(*builder.Request, interface{}) string
}).UpdateApi(request, resourceInstance)
initApi := resourceInstance.(interface {
EditValueApi(*builder.Request) string
}).EditValueApi(request)
fields := resourceInstance.(interface {
UpdateFieldsWithinComponents(*builder.Request, interface{}) interface{}
}).UpdateFieldsWithinComponents(request, resourceInstance)
return (&form.Component{}).
Init().
SetKey("editModalForm", false).
SetApi(api).
SetInitApi(initApi).
SetBody(fields).
SetLabelCol(map[string]interface{}{
"span": 6,
}).
SetWrapperCol(map[string]interface{}{
"span": 18,
})
}
// 弹窗行为
func (p *EditModal) GetActions(request *builder.Request, resourceInstance interface{}) []interface{} {
return []interface{}{
(&action.Component{}).
Init().
SetLabel("取消").
SetActionType("cancel"),
(&action.Component{}).
Init().
SetLabel("提交").
SetWithLoading(true).
SetReload("table").
SetActionType("submit").
SetType("primary", false).
SetSubmitForm("editModalForm"),
}
}

View File

@@ -0,0 +1,59 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type Enable struct {
actions.Action
}
// 初始化
func (p *Enable) Init(name string) *Enable {
// 初始化父结构
p.ParentInit()
// 行为名称当行为在表格行展示时支持js表达式
p.Name = name
// 设置按钮类型,primary | ghost | dashed | link | text | default
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 执行成功后刷新的组件
p.Reload = "table"
// 设置展示位置
p.SetOnlyOnIndexTableAlert(true)
// 当行为在表格行展示时支持js表达式
p.WithConfirm("确定要启用吗?", "启用后数据将正常使用!", "modal")
return p
}
/**
* 行为接口接收的参数,当行为在表格行展示的时候,可以配置当前行的任意字段
*
* @return array
*/
func (p *Enable) GetApiParams() []string {
return []string{
"id",
}
}
// 执行行为句柄
func (p *Enable) Handle(request *builder.Request, model *gorm.DB) interface{} {
err := model.Update("status", 1).Error
if err != nil {
return msg.Error(err.Error(), "")
}
return msg.Success("操作成功", "", "")
}

View File

@@ -0,0 +1,29 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder/actions"
)
type FormBack struct {
actions.Action
}
// 初始化
func (p *FormBack) Init() *FormBack {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "default"
// 文字
p.Name = "返回上一页"
// 行为类型
p.ActionType = "back"
// 设置展示位置
p.SetShowOnForm().SetShowOnDetail()
return p
}

View File

@@ -0,0 +1,29 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder/actions"
)
type FormExtraBack struct {
actions.Action
}
// 初始化
func (p *FormExtraBack) Init() *FormExtraBack {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "link"
// 文字
p.Name = "返回上一页"
// 行为类型
p.ActionType = "back"
// 设置展示位置
p.SetShowOnFormExtra().SetShowOnDetailExtra()
return p
}

View File

@@ -0,0 +1,29 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder/actions"
)
type FormReset struct {
actions.Action
}
// 初始化
func (p *FormReset) Init() *FormReset {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "default"
// 文字
p.Name = "重置"
// 行为类型
p.ActionType = "reset"
// 设置展示位置
p.SetShowOnForm()
return p
}

View File

@@ -0,0 +1,32 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder/actions"
)
type FormSubmit struct {
actions.Action
}
// 初始化
func (p *FormSubmit) Init() *FormSubmit {
// 初始化父结构
p.ParentInit()
// 类型
p.Type = "primary"
// 文字
p.Name = "提交"
// 行为类型
p.ActionType = "submit"
// 是否具有loading当action 的作用类型为ajax,submit时有效
p.WithLoading = true
// 设置展示位置
p.SetOnlyOnForm(true)
return p
}

View File

@@ -0,0 +1,90 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/component/admin/action"
"github.com/quarkcms/quark-go/pkg/component/admin/form"
"github.com/quarkcms/quark-go/pkg/component/admin/space"
"github.com/quarkcms/quark-go/pkg/component/admin/tpl"
)
type Import struct {
actions.Modal
}
// 初始化
func (p *Import) Init() *Import {
// 初始化父结构
p.ParentInit()
// 文字
p.Name = "导入数据"
// 设置展示位置
p.SetOnlyOnIndex(true)
return p
}
// 内容
func (p *Import) GetBody(request *builder.Request, resourceInstance interface{}) interface{} {
api := "admin/" + request.Param("resource") + "/import"
getTpl := (&tpl.Component{}).
Init().
SetBody("模板文件: <a href='/api/admin/" + request.Param("resource") + "/import/template?token=" + request.Token() + "' target='_blank'>下载模板</a>").
SetStyle(map[string]interface{}{
"marginLeft": "50px",
})
fields := []interface{}{
(&space.Component{}).
Init().
SetBody(getTpl).
SetDirection("vertical").
SetSize("middle").
SetStyle(map[string]interface{}{
"marginBottom": "20px",
}),
(&builder.AdminField{}).
File("fileId", "导入文件").
SetLimitNum(1).
SetLimitType([]string{
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
}).
SetHelp("请上传xls格式的文件"),
}
return (&form.Component{}).
Init().
SetKey("importModalForm", false).
SetApi(api).
SetBody(fields).
SetLabelCol(map[string]interface{}{
"span": 6,
}).
SetWrapperCol(map[string]interface{}{
"span": 18,
})
}
// 弹窗行为
func (p *Import) GetActions(request *builder.Request, resourceInstance interface{}) []interface{} {
return []interface{}{
(&action.Component{}).
Init().
SetLabel("取消").
SetActionType("cancel"),
(&action.Component{}).
Init().
SetLabel("提交").
SetWithLoading(true).
SetReload("table").
SetActionType("submit").
SetType("primary", false).
SetSubmitForm("importModalForm"),
}
}

View File

@@ -0,0 +1,44 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder/actions"
)
type MoreActions struct {
actions.Dropdown
}
// 初始化
func (p *MoreActions) Init(name string) *MoreActions {
// 初始化父结构
p.ParentInit()
// 下拉框箭头是否显示
p.Arrow = true
// 菜单弹出位置bottomLeft bottomCenter bottomRight topLeft topCenter topRight
p.Placement = "bottomLeft"
// 触发下拉的行为, 移动端不支持 hover,Array<click|hover|contextMenu>
p.Trigger = []string{"hover"}
// 下拉根元素的样式
p.OverlayStyle = map[string]interface{}{
"zIndex": 999,
}
// 设置按钮类型,primary | ghost | dashed | link | text | default
p.Type = "link"
// 设置按钮大小,large | middle | small | default
p.Size = "small"
// 文字
p.Name = name
// 设置展示位置
p.SetOnlyOnIndexTableRow(true)
return p
}

View File

@@ -0,0 +1,35 @@
package actions
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type SelectOptions struct {
actions.Action
}
// 执行行为句柄
func (p *SelectOptions) Handle(request *builder.Request, model *gorm.DB) interface{} {
resource := request.Param("resource")
search := request.Query("search")
lists := []map[string]interface{}{}
results := []map[string]interface{}{}
switch resource {
case "Some Field":
model.Where("Some Field = ?", search).Find(&lists)
for _, v := range lists {
item := map[string]interface{}{
"label": v["name"],
"value": v["id"],
}
results = append(results, item)
}
}
return msg.Success("操作成功", "", results)
}

View File

@@ -0,0 +1,78 @@
package actions
import (
models "github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/actions"
"github.com/quarkcms/quark-go/pkg/dal/db"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type SyncPermission struct {
actions.Action
}
// 初始化
func (p *SyncPermission) Init() *SyncPermission {
// 初始化父结构
p.ParentInit()
// 行为名称
p.Name = "同步权限"
// 执行成功后刷新的组件
p.Reload = "table"
// 是否具有loading当action 的作用类型为ajax,submit时有效
p.WithLoading = true
// 设置展示位置
p.SetOnlyOnIndex(true)
// 行为类型
p.ActionType = "ajax"
return p
}
// 执行行为句柄
func (p *SyncPermission) Handle(request *builder.Request, model *gorm.DB) interface{} {
// 获取当前权限
permissions := builder.GetRoutes()
data := []models.Permission{}
var names []string
db.Client.Model(&models.Permission{}).Pluck("name", &names)
for _, v := range permissions {
has := false
for _, nv := range names {
if nv == v {
has = true
}
}
if has == false {
permission := models.Permission{
MenuId: 0,
Name: v,
GuardName: "admin",
}
data = append(data, permission)
}
}
if len(data) == 0 {
return msg.Error("暂无新增权限!", "")
}
err := model.Create(data).Error
if err != nil {
return msg.Error(err.Error(), "")
}
err = db.Client.Model(&models.Permission{}).Where("name NOT IN ?", permissions).Delete("").Error
if err != nil {
return msg.Error(err.Error(), "")
}
return msg.Success("操作成功", "", "")
}

View File

@@ -0,0 +1,35 @@
package dashboards
import (
"github.com/quarkcms/quark-go/pkg/app/handler/admin/metrics"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/admindashboard"
)
type Index struct {
admindashboard.Template
}
// 初始化
func (p *Index) Init() interface{} {
// 初始化模板
p.TemplateInit()
p.Title = "仪表盘"
return p
}
// 内容
func (p *Index) Cards(request *builder.Request) interface{} {
return []any{
&metrics.TotalAdmin{},
&metrics.TotalLog{},
&metrics.TotalPicture{},
&metrics.TotalFile{},
&metrics.SystemInfo{},
&metrics.TeamInfo{},
}
}

View File

@@ -0,0 +1,108 @@
package login
import (
"bytes"
"github.com/dchest/captcha"
"github.com/golang-jwt/jwt/v4"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminlogin"
"github.com/quarkcms/quark-go/pkg/hash"
"github.com/quarkcms/quark-go/pkg/msg"
)
type Index struct {
adminlogin.Template
}
type LoginRequest struct {
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
CaptchaId string `json:"captchaId" form:"captchaId"`
Captcha string `json:"captcha" form:"captcha"`
}
// 初始化
func (p *Index) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 登录页面Logo
p.Logo = false
// 登录页面标题
p.Title = "QuarkGo"
// 登录页面描述
p.Description = "信息丰富的世界里,唯一稀缺的就是人类的注意力"
// 登录后跳转地址
p.Redirect = "/index?api=/api/admin/dashboard/index/index"
return p
}
// 验证码ID
func (p *Index) CaptchaId(request *builder.Request, resource *builder.Resource, templateInstance interface{}) interface{} {
return msg.Success("获取成功", "", map[string]string{
"captchaId": captcha.NewLen(4),
})
}
// 生成验证码
func (p *Index) Captcha(request *builder.Request, resource *builder.Resource, templateInstance interface{}) interface{} {
id := request.Param("id")
writer := bytes.Buffer{}
captcha.WriteImage(&writer, id, 110, 38)
return writer.Bytes()
}
// 登录方法
func (p *Index) Handle(request *builder.Request, resource *builder.Resource, templateInstance interface{}) interface{} {
loginRequest := &LoginRequest{}
if err := request.BodyParser(loginRequest); err != nil {
return msg.Error(err.Error(), "")
}
if loginRequest.CaptchaId == "" || loginRequest.Captcha == "" {
return msg.Error("验证码不能为空", "")
}
verifyResult := captcha.VerifyString(loginRequest.CaptchaId, loginRequest.Captcha)
if !verifyResult {
return msg.Error("验证码错误", "")
}
captcha.Reload(loginRequest.CaptchaId)
if loginRequest.Username == "" || loginRequest.Password == "" {
return msg.Error("用户名或密码不能为空", "")
}
adminInfo, err := (&model.Admin{}).GetInfoByUsername(loginRequest.Username)
if err != nil {
return msg.Error(err.Error(), "")
}
// 检验账号和密码
if !hash.Check(adminInfo.Password, loginRequest.Password) {
return msg.Error("用户名或密码错误", "")
}
config := builder.GetConfig()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, (&model.Admin{}).GetClaims(adminInfo))
// 获取token字符串
tokenString, err := token.SignedString([]byte(config.AppKey))
return msg.Success("获取成功", "", map[string]string{
"token": tokenString,
})
}
// 退出方法
func (p *Index) Logout(request *builder.Request, resource *builder.Resource, templateInstance interface{}) interface{} {
return msg.Success("退出成功", "", "")
}

View File

@@ -0,0 +1,42 @@
package metrics
import (
"fmt"
"runtime"
"strconv"
"time"
"github.com/quarkcms/quark-go/pkg/builder/metrics"
"github.com/quarkcms/quark-go/pkg/component/admin/descriptions"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/mem"
)
type SystemInfo struct {
metrics.AdminDescriptions
}
// 初始化
func (p *SystemInfo) Init() *SystemInfo {
p.Title = "系统信息"
p.Col = 12
return p
}
// 计算数值
func (p *SystemInfo) Calculate() *descriptions.Component {
field := &descriptions.Field{}
memory, _ := mem.VirtualMemory()
cpuPercent, _ := cpu.Percent(time.Second, false)
return p.Init().Result([]interface{}{
field.Text("系统版本").SetValue("1.0.0"),
field.Text("Fiber版本").SetValue("1.0.0"),
field.Text("Golang版本").SetValue(runtime.Version()),
field.Text("服务器操作系统").SetValue(runtime.GOOS + " " + runtime.GOARCH),
field.Text("内存信息").SetValue(strconv.FormatUint(memory.Total/(1024*1024), 10) + "MB / " + fmt.Sprintf("%.0f", memory.UsedPercent) + "%"),
field.Text("CPU使用率").SetValue(fmt.Sprintf("%.0f", cpuPercent[0]) + "%"),
})
}

View File

@@ -0,0 +1,33 @@
package metrics
import (
"github.com/quarkcms/quark-go/pkg/builder/metrics"
"github.com/quarkcms/quark-go/pkg/component/admin/descriptions"
)
type TeamInfo struct {
metrics.AdminDescriptions
}
// 初始化
func (p *TeamInfo) Init() *TeamInfo {
p.Title = "团队信息"
p.Col = 12
return p
}
// 计算数值
func (p *TeamInfo) Calculate() *descriptions.Component {
field := &descriptions.Field{}
return p.Init().Result([]interface{}{
field.Text("作者").SetValue("tangtanglove"),
field.Text("联系方式").SetValue("dai_hang_love@126.com"),
field.Text("官方网址").SetValue("<a href='https://www.quarkcms.com' target='_blank'>www.quarkcms.com</a>"),
field.Text("文档地址").SetValue("<a href='https://www.quarkcms.com' target='_blank'>查看文档</a>"),
field.Text("BUG反馈").SetValue("<a href='https://github.com/quarkcms/quark-go/issues' target='_blank'>提交BUG</a>"),
field.Text("代码仓储").SetValue("<a href='https://github.com/quarkcms/quark-go' target='_blank'>Github</a>"),
})
}

View File

@@ -0,0 +1,29 @@
package metrics
import (
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder/metrics"
"github.com/quarkcms/quark-go/pkg/component/admin/statistic"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
type TotalAdmin struct {
metrics.AdminValue
}
// 初始化
func (p *TotalAdmin) Init() *TotalAdmin {
p.Title = "管理员数量"
p.Col = 6
return p
}
// 计算数值
func (p *TotalAdmin) Calculate() *statistic.Component {
return p.
Init().
Count(db.Client.Model(&model.Admin{})).
SetValueStyle(map[string]string{"color": "#3f8600"})
}

View File

@@ -0,0 +1,31 @@
package metrics
import (
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder/metrics"
"github.com/quarkcms/quark-go/pkg/component/admin/statistic"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
type TotalFile struct {
metrics.AdminValue
}
// 初始化
// 初始化
func (p *TotalFile) Init() *TotalFile {
p.Title = "文件数量"
p.Col = 6
return p
}
// 计算数值
func (p *TotalFile) Calculate() *statistic.Component {
return p.
Init().
Count(db.Client.Model(&model.File{})).
SetValueStyle(map[string]string{"color": "#cf1322"})
}

View File

@@ -0,0 +1,29 @@
package metrics
import (
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder/metrics"
"github.com/quarkcms/quark-go/pkg/component/admin/statistic"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
type TotalLog struct {
metrics.AdminValue
}
// 初始化
func (p *TotalLog) Init() *TotalLog {
p.Title = "日志数量"
p.Col = 6
return p
}
// 计算数值
func (p *TotalLog) Calculate() *statistic.Component {
return p.
Init().
Count(db.Client.Model(&model.ActionLog{})).
SetValueStyle(map[string]string{"color": "#999999"})
}

View File

@@ -0,0 +1,29 @@
package metrics
import (
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder/metrics"
"github.com/quarkcms/quark-go/pkg/component/admin/statistic"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
type TotalPicture struct {
metrics.AdminValue
}
// 初始化
func (p *TotalPicture) Init() *TotalPicture {
p.Title = "图片数量"
p.Col = 6
return p
}
// 计算数值
func (p *TotalPicture) Calculate() *statistic.Component {
return p.
Init().
Count(db.Client.Model(&model.Picture{})).
SetValueStyle(map[string]string{"color": "#cf1322"})
}

View File

@@ -0,0 +1,23 @@
package admin
import (
"github.com/quarkcms/quark-go/pkg/app/handler/admin/dashboards"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/login"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/resources"
)
// 注册服务
var Providers = []interface{}{
&login.Index{},
&dashboards.Index{},
&resources.Admin{},
&resources.Role{},
&resources.Permission{},
&resources.Menu{},
&resources.ActionLog{},
&resources.Config{},
&resources.File{},
&resources.Picture{},
&resources.WebConfig{},
&resources.Account{},
}

View File

@@ -0,0 +1,148 @@
package resources
import (
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
type Account struct {
adminresource.Template
}
// 初始化
func (p *Account) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "个人设置"
// 模型
p.Model = &model.Admin{}
return p
}
// 表单接口
func (p *Account) FormApi(request *builder.Request) string {
return "admin/account/action/change-account"
}
// 字段
func (p *Account) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
return []interface{}{
field.Image("avatar", "头像").OnlyOnForms(),
field.Text("nickname", "昵称").
SetEditable(true).
SetRules(
[]string{
"required",
},
map[string]string{
"required": "昵称必须填写",
},
),
field.Text("email", "邮箱").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "邮箱必须填写",
},
).
SetCreationRules(
[]string{
"unique:admins,email",
},
map[string]string{
"unique": "邮箱已存在",
},
).
SetUpdateRules(
[]string{
"unique:admins,email,{id}",
},
map[string]string{
"unique": "邮箱已存在",
},
),
field.Text("phone", "手机号").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "手机号必须填写",
},
).
SetCreationRules(
[]string{
"unique:admins,phone",
},
map[string]string{
"unique": "手机号已存在",
},
).
SetUpdateRules(
[]string{
"unique:admins,phone,{id}",
},
map[string]string{
"unique": "手机号已存在",
},
),
field.Radio("sex", "性别").
SetOptions(map[interface{}]interface{}{
1: "男",
2: "女",
}).SetDefault(1),
field.Password("password", "密码").
SetCreationRules(
[]string{
"required",
},
map[string]string{
"required": "密码必须填写",
},
).OnlyOnForms(),
}
}
// 行为
func (p *Account) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.ChangeAccount{}),
(&actions.FormSubmit{}).Init(),
(&actions.FormReset{}).Init(),
(&actions.FormBack{}).Init(),
(&actions.FormExtraBack{}).Init(),
}
}
// 创建页面显示前回调
func (p *Account) BeforeCreating(request *builder.Request) map[string]interface{} {
data := map[string]interface{}{}
adminInfo, _ := (&model.Admin{}).GetAuthUser(request.Token())
db.Client.
Model(p.Model).
Where("id = ?", adminInfo.Id).
First(&data)
delete(data, "password")
return data
}

View File

@@ -0,0 +1,84 @@
package resources
import (
"time"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/searches"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
"github.com/quarkcms/quark-go/pkg/component/admin/table"
"gorm.io/gorm"
)
type ActionLog struct {
adminresource.Template
}
// 初始化
func (p *ActionLog) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "操作日志"
// 模型
p.Model = &model.ActionLog{}
// 分页
p.PerPage = 10
p.WithExport = true
return p
}
// 列表查询
func (p *ActionLog) Query(request *builder.Request, query *gorm.DB) *gorm.DB {
return query.
Select("action_logs.*,admins.username").
Joins("left join admins on admins.id = action_logs.object_id").
Where("type = ?", "admin")
}
// 字段
func (p *ActionLog) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
return []interface{}{
field.ID("id", "ID"),
field.Text("username", "用户"),
field.Text("url", "行为").
SetColumn(func(column *table.Column) *table.Column {
return column.SetEllipsis(true)
}),
field.Text("ip", "IP"),
field.Datetime("created_at", "发生时间", func() interface{} {
if p.Field["created_at"] == nil {
return p.Field["created_at"]
}
return p.Field["created_at"].(time.Time).Format("2006-01-02 15:04:05")
}),
}
}
// 搜索
func (p *ActionLog) Searches(request *builder.Request) []interface{} {
return []interface{}{
(&searches.Input{}).Init("url", "行为"),
(&searches.Input{}).Init("ip", "IP"),
}
}
// 行为
func (p *ActionLog) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.Delete{}).Init("批量删除"),
(&actions.Delete{}).Init("删除"),
}
}

View File

@@ -0,0 +1,312 @@
package resources
import (
"encoding/json"
"strconv"
"strings"
"time"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/searches"
"github.com/quarkcms/quark-go/pkg/app/model"
models "github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
"github.com/quarkcms/quark-go/pkg/component/admin/table"
"github.com/quarkcms/quark-go/pkg/dal/db"
"github.com/quarkcms/quark-go/pkg/hash"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type Admin struct {
adminresource.Template
}
// 初始化
func (p *Admin) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "管理员"
// 模型
p.Model = &model.Admin{}
// 分页
p.PerPage = 10
p.WithExport = true
return p
}
// 字段
func (p *Admin) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
// 角色列表
roles, _ := (&model.Role{}).List()
return []interface{}{
field.ID("id", "ID"),
field.Image("avatar", "头像").OnlyOnForms(),
field.Text("username", "用户名", func() interface{} {
return "<a href='#/index?api=/api/admin/admin/edit&id=" + strconv.Itoa(p.Field["id"].(int)) + "'>" + p.Field["username"].(string) + "</a>"
}).
SetRules(
[]string{
"required",
"min:6",
"max:20",
},
map[string]string{
"required": "用户名必须填写",
"min": "用户名不能少于6个字符",
"max": "用户名不能超过20个字符",
},
).
SetCreationRules(
[]string{
"unique:admins,username",
},
map[string]string{
"unique": "用户名已存在",
},
).
SetUpdateRules(
[]string{
"unique:admins,username,{id}",
},
map[string]string{
"unique": "用户名已存在",
},
),
field.Checkbox("role_ids", "角色").
SetOptions(roles).
OnlyOnForms(),
field.Text("nickname", "昵称").
SetEditable(true).
SetRules(
[]string{
"required",
},
map[string]string{
"required": "昵称必须填写",
},
),
field.Text("email", "邮箱").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "邮箱必须填写",
},
).
SetCreationRules(
[]string{
"unique:admins,email",
},
map[string]string{
"unique": "邮箱已存在",
},
).
SetUpdateRules(
[]string{
"unique:admins,email,{id}",
},
map[string]string{
"unique": "邮箱已存在",
},
),
field.Text("phone", "手机号").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "手机号必须填写",
},
).
SetCreationRules(
[]string{
"unique:admins,phone",
},
map[string]string{
"unique": "手机号已存在",
},
).
SetUpdateRules(
[]string{
"unique:admins,phone,{id}",
},
map[string]string{
"unique": "手机号已存在",
},
),
field.Radio("sex", "性别").
SetOptions(map[interface{}]interface{}{
1: "男",
2: "女",
}).SetDefault(1).
SetColumn(func(column *table.Column) *table.Column {
return column.SetFilters(true)
}),
field.Password("password", "密码").
SetCreationRules(
[]string{
"required",
},
map[string]string{
"required": "密码必须填写",
},
).OnlyOnForms(),
field.Datetime("last_login_time", "最后登录时间", func() interface{} {
if p.Field["last_login_time"] == nil {
return p.Field["last_login_time"]
}
return p.Field["last_login_time"].(time.Time).Format("2006-01-02 15:04:05")
}).OnlyOnIndex(),
field.Switch("status", "状态").
SetTrueValue("正常").
SetFalseValue("禁用").
SetEditable(true).
SetDefault(true),
}
}
// 搜索
func (p *Admin) Searches(request *builder.Request) []interface{} {
return []interface{}{
(&searches.Input{}).Init("username", "用户名"),
(&searches.Input{}).Init("nickname", "昵称"),
(&searches.Status{}).Init(),
(&searches.DateTimeRange{}).Init("last_login_time", "登录时间"),
}
}
// 行为
func (p *Admin) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.Import{}).Init(),
(&actions.CreateLink{}).Init(p.Title),
(&actions.Delete{}).Init("批量删除"),
(&actions.Disable{}).Init("批量禁用"),
(&actions.Enable{}).Init("批量启用"),
(&actions.DetailLink{}).Init("详情"),
(&actions.MoreActions{}).Init("更多").SetActions([]interface{}{
(&actions.EditLink{}).Init("编辑"),
(&actions.Delete{}).Init("删除"),
}),
(&actions.FormSubmit{}).Init(),
(&actions.FormReset{}).Init(),
(&actions.FormBack{}).Init(),
(&actions.FormExtraBack{}).Init(),
}
}
// 编辑页面显示前回调
func (p *Admin) BeforeEditing(request *builder.Request, data map[string]interface{}) map[string]interface{} {
delete(data, "password")
roleIds := []int{}
db.Client.
Model(&model.ModelHasRole{}).
Where("model_id = ?", data["id"]).
Where("model_type = ?", "admin").
Pluck("role_id", &roleIds)
data["role_ids"] = roleIds
return data
}
// 保存数据前回调
func (p *Admin) BeforeSaving(request *builder.Request, submitData map[string]interface{}) (map[string]interface{}, error) {
// 加密密码
if submitData["password"] != nil {
submitData["password"] = hash.Make(submitData["password"].(string))
}
// 暂时清理role_ids
delete(submitData, "role_ids")
return submitData, nil
}
// 保存后回调
func (p *Admin) AfterSaved(request *builder.Request, model *gorm.DB) interface{} {
data := map[string]interface{}{}
json.Unmarshal(request.Body(), &data)
if data["role_ids"] == nil {
if model.Error != nil {
return msg.Error(model.Error.Error(), "")
}
return msg.Success("操作成功!", strings.Replace("/index?api="+adminresource.IndexRoute, ":resource", request.Param("resource"), -1), "")
}
var result *gorm.DB
if request.IsCreating() {
last := map[string]interface{}{}
model.Order("id desc").First(&last) // hack
roleData := []map[string]interface{}{}
for _, v := range data["role_ids"].([]interface{}) {
item := map[string]interface{}{
"role_id": v,
"model_type": "admin",
"model_id": last["id"],
}
roleData = append(roleData, item)
}
if len(roleData) > 0 {
// 同步角色
result = db.Client.Model(&models.ModelHasRole{}).Create(roleData)
}
} else {
// 同步角色
id := data["id"].(float64)
roleData := []map[string]interface{}{}
// 先清空用户对应的角色
db.Client.Model(&models.ModelHasRole{}).Where("model_id = ?", id).Where("model_type = ?", "admin").Delete("")
for _, v := range data["role_ids"].([]interface{}) {
item := map[string]interface{}{
"role_id": v,
"model_type": "admin",
"model_id": int(id),
}
roleData = append(roleData, item)
}
if len(roleData) > 0 {
// 同步角色
result = db.Client.Model(&models.ModelHasRole{}).Create(roleData)
}
}
if result.Error != nil {
return msg.Error(result.Error.Error(), "")
}
return msg.Success("操作成功!", strings.Replace("/index?api="+adminresource.IndexRoute, ":resource", request.Param("resource"), -1), "")
}

View File

@@ -0,0 +1,139 @@
package resources
import (
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/searches"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
)
type Config struct {
adminresource.Template
}
// 初始化
func (p *Config) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "配置"
// 模型
p.Model = &model.Config{}
// 分页
p.PerPage = 10
return p
}
// 字段
func (p *Config) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
return []interface{}{
field.ID("id", "ID"),
field.Text("title", "标题").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "标题必须填写",
},
),
field.Text("name", "名称").
SetEditable(true).
SetRules(
[]string{
"required",
},
map[string]string{
"required": "名称必须填写",
},
).
SetCreationRules(
[]string{
"unique:configs,name",
},
map[string]string{
"unique": "名称已存在",
},
).
SetUpdateRules(
[]string{
"unique:configs,name,{id}",
},
map[string]string{
"unique": "名称已存在",
},
),
field.Radio("type", "表单类型").
SetOptions(map[interface{}]interface{}{
"text": "输入框",
"textarea": "文本域",
"picture": "图片",
"file": "文件",
"switch": "开关",
}).
SetDefault("text").
OnlyOnForms(),
field.Text("sort", "排序").
SetEditable(true).
SetDefault(0).
SetHelp("值越小越靠前").
OnlyOnForms(),
field.Text("group_name", "分组名称").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "分组名称必须填写",
},
).OnlyOnForms(),
field.Text("remark", "备注").
OnlyOnForms(),
field.Switch("status", "状态").
SetTrueValue("正常").
SetFalseValue("禁用").
SetEditable(true).
SetDefault(true),
}
}
// 搜索
func (p *Config) Searches(request *builder.Request) []interface{} {
return []interface{}{
(&searches.Input{}).Init("title", "标题"),
(&searches.Input{}).Init("name", "名称"),
(&searches.Status{}).Init(),
}
}
// 行为
func (p *Config) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.CreateDrawer{}).Init(p.Title),
(&actions.Delete{}).Init("批量删除"),
(&actions.Disable{}).Init("批量禁用"),
(&actions.Enable{}).Init("批量启用"),
(&actions.ChangeStatus{}).Init(),
(&actions.EditDrawer{}).Init("编辑"),
(&actions.Delete{}).Init("删除"),
(&actions.FormSubmit{}).Init(),
(&actions.FormReset{}).Init(),
(&actions.FormBack{}).Init(),
(&actions.FormExtraBack{}).Init(),
}
}

View File

@@ -0,0 +1,72 @@
package resources
import (
"time"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/searches"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
"github.com/quarkcms/quark-go/pkg/component/admin/table"
)
type File struct {
adminresource.Template
}
// 初始化
func (p *File) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "文件"
// 模型
p.Model = &model.File{}
// 分页
p.PerPage = 10
return p
}
// 字段
func (p *File) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
return []interface{}{
field.ID("id", "ID"),
field.Text("name", "名称"),
field.Text("size", "大小").
SetColumn(func(column *table.Column) *table.Column {
return column.SetSorter(true)
}),
field.Text("ext", "扩展名"),
field.Datetime("created_at", "上传时间", func() interface{} {
if p.Field["created_at"] == nil {
return p.Field["created_at"]
}
return p.Field["created_at"].(time.Time).Format("2006-01-02 15:04:05")
}),
}
}
// 搜索
func (p *File) Searches(request *builder.Request) []interface{} {
return []interface{}{
(&searches.Input{}).Init("name", "名称"),
(&searches.DateTimeRange{}).Init("created_at", "上传时间"),
}
}
// 行为
func (p *File) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.Delete{}).Init("批量删除"),
(&actions.Delete{}).Init("删除"),
}
}

View File

@@ -0,0 +1,207 @@
package resources
import (
"encoding/json"
"strings"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/searches"
models "github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
"github.com/quarkcms/quark-go/pkg/dal/db"
"github.com/quarkcms/quark-go/pkg/lister"
"github.com/quarkcms/quark-go/pkg/msg"
"gorm.io/gorm"
)
type Menu struct {
adminresource.Template
}
// 初始化
func (p *Menu) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "菜单"
// 模型
p.Model = &models.Menu{}
// 分页
p.PerPage = false
// 默认排序
p.IndexOrder = "sort asc"
return p
}
// 字段
func (p *Menu) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
// 权限列表
permissions, _ := (&models.Permission{}).List()
// 菜单列表
menus, _ := (&models.Menu{}).OrderedList()
return []interface{}{
field.Hidden("id", "ID"), // 列表读取且不展示的字段
field.Hidden("pid", "PID").OnlyOnIndex(), // 列表读取且不展示的字段
field.Text("name", "名称").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "名称必须填写",
},
),
field.Text("guard_name", "GuardName").
SetDefault("admin").
OnlyOnForms(),
field.Icon("icon", "图标").OnlyOnForms(),
field.Radio("type", "渲染组件").
SetOptions(map[interface{}]interface{}{
"default": "无组件",
"engine": "引擎组件",
}).SetDefault("engine"),
field.Text("path", "路由").
SetEditable(true).
SetHelp("前端路由或后端api"),
field.Select("pid", "父节点").
SetOptions(menus).
SetDefault(0).
OnlyOnForms(),
field.Number("sort", "排序").
SetEditable(true).
SetDefault(0),
field.Select("permission_ids", "绑定权限").
SetMode("tags").
SetOptions(permissions).
OnlyOnForms(),
field.Switch("status", "状态").
SetTrueValue("正常").
SetFalseValue("禁用").
SetEditable(true).
SetDefault(true),
}
}
// 搜索
func (p *Menu) Searches(request *builder.Request) []interface{} {
return []interface{}{
(&searches.Input{}).Init("name", "名称"),
(&searches.Input{}).Init("path", "路由"),
(&searches.Status{}).Init(),
}
}
// 行为
func (p *Menu) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.CreateDrawer{}).Init(p.Title),
(&actions.Delete{}).Init("批量删除"),
(&actions.Disable{}).Init("批量禁用"),
(&actions.Enable{}).Init("批量启用"),
(&actions.ChangeStatus{}).Init(),
(&actions.EditDrawer{}).Init("编辑"),
(&actions.Delete{}).Init("删除"),
(&actions.FormSubmit{}).Init(),
(&actions.FormReset{}).Init(),
(&actions.FormBack{}).Init(),
(&actions.FormExtraBack{}).Init(),
}
}
// 列表页面显示前回调
func (p *Menu) BeforeIndexShowing(request *builder.Request, list []map[string]interface{}) []interface{} {
data := request.AllQuerys()
if search, ok := data["search"].(map[string]interface{}); ok == true && search != nil {
result := []interface{}{}
for _, v := range list {
result = append(result, v)
}
return result
}
// 转换成树形表格
tree, _ := lister.ListToTree(list, "id", "pid", "children", 0)
return tree
}
// 编辑页面显示前回调
func (p *Menu) BeforeEditing(request *builder.Request, data map[string]interface{}) map[string]interface{} {
id := request.Query("id", "")
if id != "" {
menus := []int{}
db.Client.
Model(&models.Permission{}).
Where("menu_id = ?", id).
Pluck("id", &menus)
data["permission_ids"] = menus
}
return data
}
// 保存数据前回调
func (p *Menu) BeforeSaving(request *builder.Request, submitData map[string]interface{}) interface{} {
// 暂时清理permission_ids
delete(submitData, "permission_ids")
return submitData
}
// 保存后回调
func (p *Menu) AfterSaved(request *builder.Request, model *gorm.DB) interface{} {
var result *gorm.DB
data := map[string]interface{}{}
json.Unmarshal(request.Body(), &data)
id := 0
if request.IsCreating() {
last := map[string]interface{}{}
result = model.Order("id desc").First(&last) // 获取最后一条记录的id
id = last["id"].(int)
} else {
id = int(data["id"].(float64))
result = db.Client.
Model(&models.Permission{}).
Where("menu_id = ?", id).
Update("menu_id", 0)
}
if data["permission_ids"] != nil {
result = db.Client.
Model(&models.Permission{}).
Where("id In ?", data["permission_ids"]).
Update("menu_id", id)
}
if result.Error != nil {
return msg.Error(result.Error.Error(), "")
}
return msg.Success("操作成功!", strings.Replace("/index?api="+adminresource.IndexRoute, ":resource", request.Param("resource"), -1), "")
}

View File

@@ -0,0 +1,90 @@
package resources
import (
"time"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/searches"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
)
type Permission struct {
adminresource.Template
}
// 初始化
func (p *Permission) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "权限"
// 模型
p.Model = &model.Permission{}
// 分页
p.PerPage = 10
return p
}
// 字段
func (p *Permission) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
return []interface{}{
field.ID("id", "ID"),
field.Text("name", "名称").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "名称必须填写",
},
),
field.Text("guard_name", "GuardName").SetDefault("admin"),
field.Datetime("created_at", "创建时间", func() interface{} {
if p.Field["created_at"] == nil {
return p.Field["created_at"]
}
return p.Field["created_at"].(time.Time).Format("2006-01-02 15:04:05")
}).OnlyOnIndex(),
field.Datetime("updated_at", "更新时间", func() interface{} {
if p.Field["updated_at"] == nil {
return p.Field["updated_at"]
}
return p.Field["updated_at"].(time.Time).Format("2006-01-02 15:04:05")
}).OnlyOnIndex(),
}
}
// 搜索
func (p *Permission) Searches(request *builder.Request) []interface{} {
return []interface{}{
(&searches.Input{}).Init("name", "名称"),
}
}
// 行为
func (p *Permission) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.SyncPermission{}).Init(),
(&actions.CreateModal{}).Init(p.Title),
(&actions.Delete{}).Init("批量删除"),
(&actions.EditModal{}).Init("编辑"),
(&actions.Delete{}).Init("删除"),
(&actions.FormSubmit{}).Init(),
(&actions.FormReset{}).Init(),
(&actions.FormBack{}).Init(),
(&actions.FormExtraBack{}).Init(),
}
}

View File

@@ -0,0 +1,80 @@
package resources
import (
"time"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/searches"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
"github.com/quarkcms/quark-go/pkg/component/admin/table"
)
type Picture struct {
adminresource.Template
}
// 初始化
func (p *Picture) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "图片"
// 模型
p.Model = &model.Picture{}
// 分页
p.PerPage = 10
return p
}
// 字段
func (p *Picture) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
return []interface{}{
field.ID("id", "ID"),
field.Text("path", "显示", func() interface{} {
return "<img src='" + (&model.Picture{}).GetPath(p.Field["id"]) + "' width=50 height=50 />"
}),
field.Text("name", "名称").SetColumn(func(column *table.Column) *table.Column {
return column.SetEllipsis(true)
}),
field.Text("size", "大小").
SetColumn(func(column *table.Column) *table.Column {
return column.SetSorter(true)
}),
field.Text("width", "宽度"),
field.Text("height", "高度"),
field.Text("ext", "扩展名"),
field.Datetime("created_at", "上传时间", func() interface{} {
if p.Field["created_at"] == nil {
return p.Field["created_at"]
}
return p.Field["created_at"].(time.Time).Format("2006-01-02 15:04:05")
}),
}
}
// 搜索
func (p *Picture) Searches(request *builder.Request) []interface{} {
return []interface{}{
(&searches.Input{}).Init("name", "名称"),
(&searches.DateTimeRange{}).Init("created_at", "上传时间"),
}
}
// 行为
func (p *Picture) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.Delete{}).Init("批量删除"),
(&actions.Delete{}).Init("删除"),
}
}

View File

@@ -0,0 +1,222 @@
package resources
import (
"encoding/json"
"errors"
"time"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/searches"
models "github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
"github.com/quarkcms/quark-go/pkg/dal/db"
"gorm.io/gorm"
)
type Role struct {
adminresource.Template
}
// 初始化
func (p *Role) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "角色"
// 模型
p.Model = &models.Role{}
// 分页
p.PerPage = 10
return p
}
// 字段
func (p *Role) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
treeData, _ := (&models.Menu{}).Tree()
return []interface{}{
field.ID("id", "ID"),
field.Text("name", "名称").
SetRules(
[]string{
"required",
},
map[string]string{
"required": "名称必须填写",
},
),
field.Text("guard_name", "GuardName").SetDefault("admin"),
field.Tree("menu_ids", "权限").SetData(treeData).OnlyOnForms(),
field.Datetime("created_at", "创建时间", func() interface{} {
if p.Field["created_at"] == nil {
return p.Field["created_at"]
}
return p.Field["created_at"].(time.Time).Format("2006-01-02 15:04:05")
}).OnlyOnIndex(),
field.Datetime("updated_at", "更新时间", func() interface{} {
if p.Field["updated_at"] == nil {
return p.Field["updated_at"]
}
return p.Field["updated_at"].(time.Time).Format("2006-01-02 15:04:05")
}).OnlyOnIndex(),
}
}
// 搜索
func (p *Role) Searches(request *builder.Request) []interface{} {
return []interface{}{
(&searches.Input{}).Init("name", "名称"),
}
}
// 行为
func (p *Role) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.CreateLink{}).Init(p.Title),
(&actions.Delete{}).Init("批量删除"),
(&actions.EditLink{}).Init("编辑"),
(&actions.Delete{}).Init("删除"),
(&actions.FormSubmit{}).Init(),
(&actions.FormReset{}).Init(),
(&actions.FormBack{}).Init(),
(&actions.FormExtraBack{}).Init(),
}
}
// 编辑页面显示前回调
func (p *Role) BeforeEditing(request *builder.Request, data map[string]interface{}) map[string]interface{} {
id := request.Query("id", "")
menus := []map[string]interface{}{}
db.Client.Model(&models.Menu{}).Find(&menus)
checkedMenus := []int{}
for _, v := range menus {
var permissionIds []int
db.Client.
Model(&models.Permission{}).
Where("menu_id", v["id"]).
Pluck("id", &permissionIds)
if len(permissionIds) > 0 {
roleHasPermission := map[string]interface{}{}
db.Client.
Model(&models.RoleHasPermission{}).
Where("permission_id IN ?", permissionIds).
Where("role_id", id).
First(&roleHasPermission)
if len(roleHasPermission) > 0 {
checkedMenus = append(checkedMenus, v["id"].(int))
}
}
}
data["menu_ids"] = checkedMenus
return data
}
// 保存数据前回调
func (p *Role) BeforeSaving(request *builder.Request, submitData map[string]interface{}) interface{} {
// 根据菜单id获取所有权限
var permissionIds []int
db.Client.
Model(&models.Permission{}).
Where("menu_id IN ?", submitData["menu_ids"]).
Pluck("id", &permissionIds)
if len(permissionIds) == 0 {
return errors.New("获取的权限为空,请在菜单管理中绑定权限")
}
delete(submitData, "menu_ids")
return submitData
}
// 保存后回调
func (p *Role) AfterSaved(request *builder.Request, model *gorm.DB) interface{} {
data := map[string]interface{}{}
json.Unmarshal(request.Body(), &data)
// 根据菜单id获取所有权限
var permissionIds []int
db.Client.
Model(&models.Permission{}).
Where("menu_id IN ?", data["menu_ids"]).
Pluck("id", &permissionIds)
if len(permissionIds) == 0 {
return errors.New("获取的权限为空,请先在菜单管理中绑定权限")
}
var result interface{}
if request.IsCreating() {
lastRole := map[string]interface{}{}
model.Order("id desc").First(&lastRole) // hack
// 同步权限
result = p.syncPermissions(lastRole["id"].(int), permissionIds)
} else {
// 同步权限
id := data["id"].(float64)
result = p.syncPermissions(int(id), permissionIds)
}
return result
}
// 保存后回调
func (p *Role) syncPermissions(roleId int, permissionIds []int) *gorm.DB {
permissionIds = p.arrayFilter(permissionIds)
// 先清空此角色的权限
db.Client.Model(&models.RoleHasPermission{}).Where("role_id", roleId).Delete("")
data := []map[string]interface{}{}
for _, v := range permissionIds {
permission := map[string]interface{}{
"role_id": roleId,
"permission_id": v,
}
data = append(data, permission)
}
return db.Client.Model(&models.RoleHasPermission{}).Create(data)
}
// 数组去重
func (p *Role) arrayFilter(list []int) []int {
var x []int = []int{}
for _, i := range list {
if len(x) == 0 {
x = append(x, i)
} else {
for k, v := range x {
if i == v {
break
}
if k == len(x)-1 {
x = append(x, i)
}
}
}
}
return x
}

View File

@@ -0,0 +1,171 @@
package resources
import (
"encoding/json"
"strings"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/actions"
models "github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template/adminresource"
"github.com/quarkcms/quark-go/pkg/component/admin/tabs"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
type WebConfig struct {
adminresource.Template
}
// 初始化
func (p *WebConfig) Init() interface{} {
// 初始化模板
p.TemplateInit()
// 标题
p.Title = "网站配置"
// 模型
p.Model = &models.Config{}
return p
}
// 表单接口
func (p *WebConfig) FormApi(request *builder.Request) string {
return "admin/webConfig/action/change-web-config"
}
// 字段
func (p *WebConfig) Fields(request *builder.Request) []interface{} {
field := &builder.AdminField{}
groupNames := []string{}
db.Client.Model(p.Model).Where("status = ?", 1).Distinct("group_name").Pluck("group_name", &groupNames)
tabPanes := []interface{}{}
for _, groupName := range groupNames {
configs := []map[string]interface{}{}
db.Client.
Model(p.Model).
Where("status = ?", 1).
Where("group_name = ?", groupName).
Order("sort asc").
Find(&configs)
fields := []interface{}{}
for _, config := range configs {
remark, ok := config["remark"].(string)
if ok == false {
remark = ""
}
switch config["type"] {
case "text":
getField := field.
Text(config["name"], config["title"]).SetExtra(remark)
fields = append(fields, getField)
case "textarea":
getField := field.
TextArea(config["name"], config["title"]).SetExtra(remark)
fields = append(fields, getField)
case "file":
getField := field.
File(config["name"], config["title"]).
SetButton("上传" + config["title"].(string)).
SetExtra(remark)
fields = append(fields, getField)
case "picture":
getField := field.
Image(config["name"], config["title"]).
SetButton("上传" + config["title"].(string)).
SetExtra(remark)
fields = append(fields, getField)
case "switch":
getField := field.
Switch(config["name"].(string), config["title"].(string)).
SetTrueValue("正常").
SetFalseValue("禁用").
SetExtra(remark)
fields = append(fields, getField)
}
}
tabPane := (&tabs.TabPane{}).
Init().
SetTitle(groupName).
SetBody(fields)
tabPanes = append(tabPanes, tabPane)
}
return tabPanes
}
// 行为
func (p *WebConfig) Actions(request *builder.Request) []interface{} {
return []interface{}{
(&actions.ChangeWebConfig{}),
(&actions.FormSubmit{}).Init(),
(&actions.FormReset{}).Init(),
(&actions.FormBack{}).Init(),
(&actions.FormExtraBack{}).Init(),
}
}
// 创建页面显示前回调
func (p *WebConfig) BeforeCreating(request *builder.Request) map[string]interface{} {
configs := []map[string]interface{}{}
data := map[string]interface{}{}
db.Client.
Model(p.Model).
Where("status = ?", 1).
Find(&configs)
for _, config := range configs {
data[config["name"].(string)] = config["value"]
if config["type"] == "switch" {
if config["value"] != "0" {
data[config["name"].(string)] = true
} else {
data[config["name"].(string)] = false
}
}
if config["type"] == "picture" || config["type"] == "file" {
// json字符串
if strings.Contains(config["value"].(string), "{") {
var jsonData interface{}
json.Unmarshal([]byte(config["value"].(string)), &jsonData)
// 如果为map
if mapData, ok := jsonData.(map[string]interface{}); ok {
data[config["name"].(string)] = mapData
}
// 如果为数组返回第一个key的path
if arrayData, ok := jsonData.([]map[string]interface{}); ok {
data[config["name"].(string)] = arrayData
}
}
}
}
return data
}

View File

@@ -0,0 +1,31 @@
package searches
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/searches"
"gorm.io/gorm"
)
type DateTimeRange struct {
searches.DatetimeRange
}
// 初始化
func (p *DateTimeRange) Init(column string, name string) *DateTimeRange {
p.ParentInit()
p.Column = column
p.Name = name
return p
}
// 执行查询
func (p *DateTimeRange) Apply(request *builder.Request, query *gorm.DB, value interface{}) *gorm.DB {
values, ok := value.(map[string]interface{})
if ok == false {
return query
}
return query.Where(p.Column+" BETWEEN ? AND ?", values["0"], values["1"])
}

View File

@@ -0,0 +1,25 @@
package searches
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/searches"
"gorm.io/gorm"
)
type Input struct {
searches.Search
}
// 初始化
func (p *Input) Init(column string, name string) *Input {
p.ParentInit()
p.Column = column
p.Name = name
return p
}
// 执行查询
func (p *Input) Apply(request *builder.Request, query *gorm.DB, value interface{}) *gorm.DB {
return query.Where(p.Column+" LIKE ?", "%"+value.(string)+"%")
}

View File

@@ -0,0 +1,41 @@
package searches
import (
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/searches"
"gorm.io/gorm"
)
type Status struct {
searches.Select
}
// 初始化
func (p *Status) Init() *Status {
p.ParentInit()
p.Name = "状态"
return p
}
// 执行查询
func (p *Status) Apply(request *builder.Request, query *gorm.DB, value interface{}) *gorm.DB {
var status int
if value.(string) == "on" {
status = 1
} else {
status = 0
}
return query.Where("status = ?", status)
}
// 属性
func (p *Status) Options(request *builder.Request) map[interface{}]interface{} {
return map[interface{}]interface{}{
"on": "正常",
"off": "禁用",
}
}

View File

View File

@@ -0,0 +1,66 @@
package install
import (
"os"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
// 判断路径是否存在
func PathExist(path string) bool {
_, err := os.Stat(path) //os.Stat获取文件信息
if err != nil {
if os.IsExist(err) {
return true
}
return false
}
return true
}
// 执行安装操作
func Handle(request *builder.Request) error {
// 如果锁定文件存在则不执行安装步骤
if PathExist("install.lock") {
return nil
}
// 迁移数据
db.Client.AutoMigrate(
&model.ActionLog{},
&model.Admin{},
&model.Config{},
&model.Menu{},
&model.File{},
&model.FileCategory{},
&model.Picture{},
&model.PictureCategory{},
&model.Permission{},
&model.Role{},
&model.ModelHasRole{},
&model.RoleHasPermission{},
&model.ModelHasPermission{},
)
// 如果超级管理员不存在,初始化数据库数据
adminInfo, err := (&model.Admin{}).GetInfoById(1)
if err != nil && err.Error() != "record not found" {
panic(err)
}
if adminInfo.Id == 0 {
// 数据填充
(&model.Admin{}).Seeder()
(&model.Config{}).Seeder()
(&model.Menu{}).Seeder()
}
// 创建锁定文件
file, _ := os.Create("install.lock")
file.Close()
return nil
}

View File

@@ -0,0 +1,71 @@
package middleware
import (
"errors"
"github.com/quarkcms/quark-go/pkg/app/handler/admin/login"
"github.com/quarkcms/quark-go/pkg/app/model"
"github.com/quarkcms/quark-go/pkg/builder"
)
// 中间件
func Handle(request *builder.Request) error {
loginIndex := (&login.Index{}).Init()
// 获取登录模板定义的路由
loginIndexRoutes := loginIndex.(interface {
GetRoutes() []*builder.Route
}).GetRoutes()
inLoginRoute := false
for _, v := range loginIndexRoutes {
if v.Path == request.FullPath() {
inLoginRoute = true
}
}
// 排除登录路由
if inLoginRoute {
return nil
}
// 获取登录管理员信息
adminInfo, err := (&model.Admin{}).GetAuthUser(request.Token())
if err != nil {
return err
}
guardName := adminInfo.GuardName
if guardName != "admin" {
return errors.New("401 Unauthozied")
}
// 管理员id
if adminInfo.Id != 1 {
permissions, err := (&model.Permission{}).GetListByAdminId(adminInfo.Id)
if err != nil {
return errors.New("403 Forbidden")
}
hasPermission := false
for _, v := range permissions {
if "/"+v.Name == request.Path() {
hasPermission = true
}
}
if !hasPermission {
return errors.New("403 Forbidden")
}
}
// 记录操作日志
(&model.ActionLog{}).InsertGetId(&model.ActionLog{
ObjectId: adminInfo.Id,
Url: request.Path(),
Ip: request.IP(),
Type: "admin",
})
return nil
}

View File

@@ -0,0 +1,28 @@
package model
import (
"time"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
// 字段
type ActionLog struct {
Id int `json:"id" gorm:"autoIncrement"`
ObjectId int `json:"object_id" gorm:"size:11;not null"`
Username string `json:"username" gorm:"<-:false"`
Url string `json:"url" gorm:"size:500;not null"`
Remark string `json:"remark" gorm:"size:255;not null"`
Ip string `json:"ip" gorm:"size:100;not null"`
Type string `json:"type" gorm:"size:100;not null"`
Status int `json:"status" gorm:"size:1;not null;default:1"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// 插入数据
func (model *ActionLog) InsertGetId(data *ActionLog) (id int, Error error) {
err := db.Client.Create(data).Error
return data.Id, err
}

134
pkg/app/model/admin.go Normal file
View File

@@ -0,0 +1,134 @@
package model
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/dal/db"
"github.com/quarkcms/quark-go/pkg/hash"
"gorm.io/gorm"
)
// 字段
type Admin struct {
Id int `json:"id" gorm:"autoIncrement"`
Username string `json:"username" gorm:"size:20;index:admins_username_unique,unique;not null"`
Nickname string `json:"nickname" gorm:"size:200;not null"`
Sex int `json:"sex" gorm:"size:4;not null;default:1"`
Email string `json:"email" gorm:"size:50;index:admins_email_unique,unique;not null"`
Phone string `json:"phone" gorm:"size:11;index:admins_phone_unique,unique;not null"`
Password string `json:"password" gorm:"size:255;not null"`
Avatar string `json:"avatar" gorm:"size:1000"`
LastLoginIp string `json:"last_login_ip" gorm:"size:255"`
LastLoginTime time.Time `json:"last_login_time"`
Status int `json:"status" gorm:"size:1;not null;default:1"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at"`
}
// 管理员JWT结构体
type AdminClaims struct {
Id int `json:"id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Sex int `json:"sex"`
Email string `json:"email"`
Phone string `json:"phone"`
Avatar string `json:"avatar"`
GuardName string `json:"guard_name"`
jwt.RegisteredClaims
}
// 管理员Seeder
func (model *Admin) Seeder() {
seeders := []Admin{
{Username: "administrator", Nickname: "超级管理员", Email: "admin@yourweb.com", Phone: "10086", Password: hash.Make("123456"), Sex: 1, Status: 1},
}
db.Client.Create(&seeders)
}
// 获取管理员JWT信息
func (model *Admin) GetClaims(adminInfo *Admin) (adminClaims *AdminClaims) {
adminClaims = &AdminClaims{
adminInfo.Id,
adminInfo.Username,
adminInfo.Nickname,
adminInfo.Sex,
adminInfo.Email,
adminInfo.Phone,
adminInfo.Avatar,
"admin",
jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 过期时间默认24小时
IssuedAt: jwt.NewNumericDate(time.Now()), // 颁发时间
NotBefore: jwt.NewNumericDate(time.Now()), // 不早于时间
Issuer: "QuarkGo", // 颁发人
Subject: "Admin Token", // 主题信息
},
}
return adminClaims
}
// 获取当前认证的用户信息默认参数为tokenString
func (model *Admin) GetAuthUser(authValue interface{}) (adminClaims *AdminClaims, Error error) {
config := builder.GetConfig()
token, err := jwt.ParseWithClaims(authValue.(string), &AdminClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(config.AppKey), nil
})
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, errors.New("token格式错误")
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
return nil, errors.New("token已过期")
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, errors.New("token未生效")
} else {
return nil, err
}
}
}
if claims, ok := token.Claims.(*AdminClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("token不可用")
}
// 通过ID获取管理员信息
func (model *Admin) GetInfoById(id interface{}) (admin *Admin, Error error) {
err := db.Client.Where("status = ?", 1).Where("id = ?", id).First(&admin).Error
return admin, err
}
// 通过用户名获取管理员信息
func (model *Admin) GetInfoByUsername(username string) (admin *Admin, Error error) {
err := db.Client.Where("status = ?", 1).Where("username = ?", username).First(&admin).Error
return admin, err
}
// 通过ID获取管理员拥有的菜单列表
func (model *Admin) GetMenuListById(id interface{}) (menuList interface{}, Error error) {
return (&Menu{}).GetListByAdminId(id.(int))
}
// 更新最后一次登录数据
func (model *Admin) UpdateLastLogin(uid int, lastLoginIp string, lastLoginTime time.Time) error {
data := Admin{
LastLoginIp: lastLoginIp,
LastLoginTime: lastLoginTime,
}
return db.Client.
Where("id = ?", uid).
Updates(&data).Error
}

66
pkg/app/model/config.go Normal file
View File

@@ -0,0 +1,66 @@
package model
import (
"time"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
// 字段
type Config struct {
Id int `json:"id" gorm:"autoIncrement"`
Title string `json:"title" gorm:"size:255;not null"`
Type string `json:"type" gorm:"size:20;not null"`
Name string `json:"name" gorm:"size:255;not null"`
Sort int `json:"sort" gorm:"size:11;default:0"`
GroupName string `json:"group_name" gorm:"size:255;not null"`
Value string `json:"value" gorm:"size:2000"`
Remark string `json:"remark" gorm:"size:100;not null"`
Status int `json:"status" gorm:"size:1;not null;default:1"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// 存储配置
var webConfig = make(map[string]string)
// 配置表
func (model *Config) Seeder() {
seeders := []Config{
{Title: "网站名称", Type: "text", Name: "WEB_SITE_NAME", Sort: 0, GroupName: "基本", Value: "QuarkCMS", Remark: "", Status: 1},
{Title: "关键字", Type: "text", Name: "WEB_SITE_KEYWORDS", Sort: 0, GroupName: "基本", Value: "QuarkCMS", Remark: "", Status: 1},
{Title: "描述", Type: "textarea", Name: "WEB_SITE_DESCRIPTION", Sort: 0, GroupName: "基本", Value: "QuarkCMS", Remark: "", Status: 1},
{Title: "Logo", Type: "picture", Name: "WEB_SITE_LOGO", Sort: 0, GroupName: "基本", Value: "", Remark: "", Status: 1},
{Title: "统计代码", Type: "textarea", Name: "WEB_SITE_SCRIPT", Sort: 0, GroupName: "基本", Value: "", Remark: "", Status: 1},
{Title: "网站域名", Type: "text", Name: "WEB_SITE_DOMAIN", Sort: 0, GroupName: "基本", Value: "", Remark: "", Status: 1},
{Title: "网站版权", Type: "text", Name: "WEB_SITE_COPYRIGHT", Sort: 0, GroupName: "基本", Value: "© Company 2018", Remark: "", Status: 1},
{Title: "开启SSL", Type: "switch", Name: "SSL_OPEN", Sort: 0, GroupName: "基本", Value: "0", Remark: "", Status: 1},
{Title: "开启网站", Type: "switch", Name: "WEB_SITE_OPEN", Sort: 0, GroupName: "基本", Value: "1", Remark: "", Status: 1},
{Title: "KeyID", Type: "text", Name: "OSS_ACCESS_KEY_ID", Sort: 0, GroupName: "阿里云存储", Value: "", Remark: "你的AccessKeyID", Status: 1},
{Title: "KeySecret", Type: "text", Name: "OSS_ACCESS_KEY_SECRET", Sort: 0, GroupName: "阿里云存储", Value: "", Remark: "你的AccessKeySecret", Status: 1},
{Title: "EndPoint", Type: "text", Name: "OSS_ENDPOINT", Sort: 0, GroupName: "阿里云存储", Value: "", Remark: "地域节点", Status: 1},
{Title: "Bucket域名", Type: "text", Name: "OSS_BUCKET", Sort: 0, GroupName: "阿里云存储", Value: "", Remark: "", Status: 1},
{Title: "自定义域名", Type: "text", Name: "OSS_MYDOMAIN", Sort: 0, GroupName: "阿里云存储", Value: "", Remark: "例如oss.web.com", Status: 1},
{Title: "开启云存储", Type: "switch", Name: "OSS_OPEN", Sort: 0, GroupName: "阿里云存储", Value: "0", Remark: "", Status: 1},
}
db.Client.Create(&seeders)
}
// 刷新配置
func (model *Config) Refresh() {
configs := []Config{}
db.Client.Where("status", 1).Find(&configs)
for _, config := range configs {
webConfig[config.Name] = config.Value
}
}
// 获取配置信息
func (model *Config) GetValue(key string) string {
if len(webConfig) == 0 {
model.Refresh()
}
return webConfig[key]
}

143
pkg/app/model/file.go Normal file
View File

@@ -0,0 +1,143 @@
package model
import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/quarkcms/quark-go/pkg/dal/db"
"github.com/xuri/excelize/v2"
)
// 字段
type File struct {
Id int `json:"id" gorm:"autoIncrement"`
ObjType string `json:"obj_type" gorm:"size:255"`
ObjId int `json:"obj_id" gorm:"size:11;default:0"`
FileCategoryId int `json:"file_category_id" gorm:"size:11;default:0"`
Sort int `json:"sort" gorm:"size:11;default:0"`
Name string `json:"name" gorm:"size:255;not null"`
Size string `json:"size" gorm:"size:20;default:0"`
Ext string `json:"ext" gorm:"size:255"`
Path string `json:"path" gorm:"size:255;not null"`
Md5 string `json:"md5" gorm:"size:255;not null"`
Status int `json:"status" gorm:"size:1;not null;default:1"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// 插入数据并返回ID
func (model *File) InsertGetId(data *File) (id int, Error error) {
err := db.Client.Create(&data).Error
return data.Id, err
}
// 获取文件路径
func (model *File) GetPath(id interface{}) string {
http, path := "", ""
webSiteDomain := (&Config{}).GetValue("WEB_SITE_DOMAIN")
WebConfig := (&Config{}).GetValue("SSL_OPEN")
if webSiteDomain != "" {
if WebConfig == "1" {
http = "https://"
} else {
http = "http://"
}
}
if getId, ok := id.(string); ok {
if strings.Contains(getId, "//") && !strings.Contains(getId, "{") {
return getId
}
if strings.Contains(getId, "./") && !strings.Contains(getId, "{") {
// 如果设置域名,则加上域名前缀
return http + webSiteDomain + strings.Replace(getId, "./storage/app/public", "/storage", -1)
}
// json字符串
if strings.Contains(getId, "{") {
var jsonData interface{}
json.Unmarshal([]byte(getId), &jsonData)
// 如果为map
if mapData, ok := jsonData.(map[string]interface{}); ok {
path = mapData["path"].(string)
}
// 如果为数组返回第一个key的path
if arrayData, ok := jsonData.([]map[string]interface{}); ok {
path = arrayData[0]["path"].(string)
}
}
if strings.Contains(path, "//") {
return path
}
if strings.Contains(path, "./") {
path = strings.Replace(path, "./storage/app/public", "/storage", -1)
}
if path != "" {
// 如果设置域名,则加上域名前缀
return http + webSiteDomain + path
}
}
file := &File{}
db.Client.Where("id", id).Where("status", 1).First(&file)
if file.Id != 0 {
path = file.Path
if strings.Contains(path, "//") {
return path
}
if strings.Contains(path, "./") {
path = strings.Replace(path, "./storage/app/public", "/storage", -1)
}
if path != "" {
// 如果设置域名,则加上域名前缀
return http + webSiteDomain + path
}
}
return ""
}
// 获取Excel文件数据
func (model *File) GetExcelData(fileId int) (data [][]interface{}, Error error) {
filePath := ""
file := &File{}
err := db.Client.Where("id", fileId).Where("status", 1).First(&file).Error
if err != nil {
return data, err
}
if file.Id != 0 {
filePath = file.Path
}
if filePath == "" {
return data, errors.New("参数错误!")
}
f, err := excelize.OpenFile(filePath)
if err != nil {
return data, err
}
defer func() {
if err := f.Close(); err != nil {
fmt.Println(err)
}
}()
rows, err := f.GetRows("Sheet1")
if err != nil {
return data, err
}
for _, row := range rows {
getRows := []interface{}{}
for _, colCell := range row {
getRows = append(getRows, colCell)
}
data = append(data, getRows)
}
return data, err
}

View File

@@ -0,0 +1,11 @@
package model
// 字段
type FileCategory struct {
Id int `json:"id" gorm:"autoIncrement"`
ObjType string `json:"obj_type" gorm:"size:100"`
ObjId int `json:"obj_id" gorm:"size:11;default:0"`
Title string `json:"title" gorm:"size:255;not null"`
Sort int `json:"sort" gorm:"size:11;default:0"`
Description string `json:"description" gorm:"size:255"`
}

198
pkg/app/model/menu.go Normal file
View File

@@ -0,0 +1,198 @@
package model
import (
"strings"
"time"
"github.com/go-basic/uuid"
"github.com/quarkcms/quark-go/pkg/dal/db"
"github.com/quarkcms/quark-go/pkg/lister"
)
// 字段
type Menu struct {
Key string `json:"key" gorm:"<-:false"`
Id int `json:"id" gorm:"autoIncrement"`
Name string `json:"name" gorm:"size:100;not null"`
GuardName string `json:"group_name" gorm:"size:100;not null"`
Icon string `json:"icon" gorm:"size:100;"`
Type string `json:"type" gorm:"size:100;not null"`
Pid int `json:"pid" gorm:"size:11;default:0"`
Sort int `json:"sort" gorm:"size:11;default:0"`
Path string `json:"path" gorm:"size:255"`
Show int `json:"show" gorm:"size:1;not null;default:1"`
Status int `json:"status" gorm:"size:1;not null;default:1"`
Locale string `json:"locale" gorm:"<-:false"`
HideInMenu bool `json:"hide_in_menu" gorm:"<-:false"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// 菜单表
func (p *Menu) Seeder() {
seeders := []Menu{
{Id: 1, Name: "控制台", GuardName: "admin", Icon: "icon-home", Type: "default", Pid: 0, Sort: 0, Path: "/dashboard", Show: 1, Status: 1},
{Id: 2, Name: "主页", GuardName: "admin", Icon: "", Type: "engine", Pid: 1, Sort: 0, Path: "/api/admin/dashboard/index/index", Show: 1, Status: 1},
{Id: 3, Name: "管理员", GuardName: "admin", Icon: "icon-admin", Type: "default", Pid: 0, Sort: 0, Path: "/admin", Show: 1, Status: 1},
{Id: 4, Name: "管理员列表", GuardName: "admin", Icon: "", Type: "engine", Pid: 3, Sort: 0, Path: "/api/admin/admin/index", Show: 1, Status: 1},
{Id: 5, Name: "权限列表", GuardName: "admin", Icon: "", Type: "engine", Pid: 3, Sort: 0, Path: "/api/admin/permission/index", Show: 1, Status: 1},
{Id: 6, Name: "角色列表", GuardName: "admin", Icon: "", Type: "engine", Pid: 3, Sort: 0, Path: "/api/admin/role/index", Show: 1, Status: 1},
{Id: 7, Name: "系统配置", GuardName: "admin", Icon: "icon-setting", Type: "default", Pid: 0, Sort: 0, Path: "/system", Show: 1, Status: 1},
{Id: 8, Name: "设置管理", GuardName: "admin", Icon: "", Type: "default", Pid: 7, Sort: 0, Path: "/system/config", Show: 1, Status: 1},
{Id: 9, Name: "网站设置", GuardName: "admin", Icon: "", Type: "engine", Pid: 8, Sort: 0, Path: "/api/admin/webConfig/setting/form", Show: 1, Status: 1},
{Id: 10, Name: "配置管理", GuardName: "admin", Icon: "", Type: "engine", Pid: 8, Sort: 0, Path: "/api/admin/config/index", Show: 1, Status: 1},
{Id: 11, Name: "菜单管理", GuardName: "admin", Icon: "", Type: "engine", Pid: 7, Sort: 0, Path: "/api/admin/menu/index", Show: 1, Status: 1},
{Id: 12, Name: "操作日志", GuardName: "admin", Icon: "", Type: "engine", Pid: 7, Sort: 0, Path: "/api/admin/actionLog/index", Show: 1, Status: 1},
{Id: 13, Name: "附件空间", GuardName: "admin", Icon: "icon-attachment", Type: "default", Pid: 0, Sort: 0, Path: "/attachment", Show: 1, Status: 1},
{Id: 14, Name: "文件管理", GuardName: "admin", Icon: "", Type: "engine", Pid: 13, Sort: 0, Path: "/api/admin/file/index", Show: 1, Status: 1},
{Id: 15, Name: "图片管理", GuardName: "admin", Icon: "", Type: "engine", Pid: 13, Sort: 0, Path: "/api/admin/picture/index", Show: 1, Status: 1},
{Id: 16, Name: "我的账号", GuardName: "admin", Icon: "icon-user", Type: "default", Pid: 0, Sort: 0, Path: "/account", Show: 1, Status: 1},
{Id: 17, Name: "个人设置", GuardName: "admin", Icon: "", Type: "engine", Pid: 16, Sort: 0, Path: "/api/admin/account/setting/form", Show: 1, Status: 1},
}
db.Client.Create(&seeders)
}
// 获取菜单的有序列表
func (model *Menu) OrderedList() (list []map[string]interface{}, Error error) {
var menus []map[string]interface{}
err := db.Client.
Model(&model).
Where("guard_name = ?", "admin").
Order("sort asc,id asc").
Find(&menus).Error
if err != nil {
return list, err
}
menuTrees, err := lister.ListToTree(menus, "id", "pid", "children", 0)
if err != nil {
return list, err
}
menuTreeList, err := lister.TreeToOrderedList(menuTrees, 0, "name", "children")
if err != nil {
return list, err
}
list = append(list, map[string]interface{}{
"label": "根节点",
"value": 0,
})
for _, v := range menuTreeList {
option := map[string]interface{}{
"label": v.((map[string]interface{}))["name"],
"value": v.(map[string]interface{})["id"],
}
list = append(list, option)
}
return list, nil
}
// 获取菜单的tree
func (model *Menu) Tree() (list []interface{}, Error error) {
menus := []Menu{}
err := db.Client.Where("status = ?", 1).Select("name", "id", "pid").Find(&menus).Error
if err != nil {
return list, err
}
menuList := []map[string]interface{}{}
for _, v := range menus {
item := map[string]interface{}{
"key": v.Id,
"pid": v.Pid,
"title": v.Name,
}
menuList = append(menuList, item)
}
return lister.ListToTree(menuList, "key", "pid", "children", 0)
}
// 通过管理员ID权限菜单
func (model *Menu) GetListByAdminId(adminId int) (menuList interface{}, Error error) {
menus := []Menu{}
var menuKey int
if adminId == 1 {
db.Client.Where("status = ?", 1).Where("guard_name", "admin").Order("sort asc").Find(&menus)
} else {
var menuIds []int
permissions, err := (&Permission{}).GetListByAdminId(adminId)
if err != nil {
return menuList, err
}
if permissions != nil {
for key, v := range permissions {
menuIds[key] = v.MenuId
}
}
var pids1 []int
// 三级查询列表
db.Client.
Where("status = ?", 1).
Where("id in (?)", menuIds).
Where("pid <> ?", 0).
Order("sort asc").
Find(&menus)
for key, v := range menus {
if v.Pid != 0 {
pids1[key] = v.Pid
}
menuKey = key
}
var pids2 []int
menu2 := []Menu{}
// 二级查询列表
db.Client.
Where("status = ?", 1).
Where("id in (?)", pids1).
Where("pid <> ?", 0).
Order("sort asc").
Find(&menu2)
for key, v := range menu2 {
if v.Pid != 0 {
pids2[key] = v.Pid
}
menuKey = menuKey + key
menus[menuKey] = v
}
menu3 := []Menu{}
// 一级查询列表
db.Client.
Where("status = ?", 1).
Where("id in (?)", pids2).
Where("pid", 0).
Order("sort asc").
Find(&menu3)
for key, v := range menu3 {
menuKey = menuKey + key
menus[menuKey] = v
}
}
for k, v := range menus {
v.Key = uuid.New()
v.Locale = "menu" + strings.Replace(v.Path, "/", ".", -1)
if v.Show == 1 {
v.HideInMenu = false
} else {
v.HideInMenu = true
}
if v.Type == "engine" {
v.Path = "/index?api=" + v.Path
}
menus[k] = v
}
return lister.ListToTree(menus, "id", "pid", "routes", 0)
}

View File

@@ -0,0 +1,63 @@
package model
import (
"time"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
// 权限
type Permission struct {
Id int `json:"id" gorm:"autoIncrement"`
MenuId int `json:"menu_id" gorm:"size:11;default:0"`
Name string `json:"name" gorm:"size:100;not null"`
GuardName string `json:"guard_name" gorm:"size:100"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// 获取列表
func (model *Permission) List() (list []map[string]interface{}, Error error) {
permissions := []Permission{}
err := db.Client.Find(&permissions).Error
if err != nil {
return list, err
}
for _, v := range permissions {
option := map[string]interface{}{
"label": v.Name,
"value": v.Id,
}
list = append(list, option)
}
return list, nil
}
// 通过权限id集合获取权限列表
func (model *Permission) GetListByIds(permissionIds []int) (permissions []Permission, Error error) {
err := db.Client.Where("id in (?)", &permissionIds).Find(&permissions).Error
return permissions, err
}
// 通过管理员ID获取权限列表
func (model *Permission) GetListByAdminId(id int) (permissions []Permission, Error error) {
// 管理员拥有的角色id集合
roleIds, err := (&ModelHasRole{}).GetRoleIdsByAdminId(id)
if err != nil {
return permissions, nil
}
// 角色拥有的权限id集合
permissionIds, err := (&RoleHasPermission{}).GetPermissionIdsByRoleIds(roleIds)
if err != nil {
return permissions, err
}
// 角色权限列表
return (&Permission{}).GetListByIds(permissionIds)
}

118
pkg/app/model/picture.go Normal file
View File

@@ -0,0 +1,118 @@
package model
import (
"encoding/json"
"strconv"
"strings"
"time"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
// 字段
type Picture struct {
Id int `json:"id" gorm:"autoIncrement"`
ObjType string `json:"obj_type" gorm:"size:255"`
ObjId int `json:"obj_id" gorm:"size:11;default:0"`
PictureCategoryId int `json:"picture_category_id" gorm:"size:11;default:0"`
Sort int `json:"sort" gorm:"size:11;default:0"`
Name string `json:"name" gorm:"size:255;not null"`
Size string `json:"size" gorm:"size:20;default:0"`
Width int `json:"width" gorm:"size:11;default:0"`
Height int `json:"height" gorm:"size:11;default:0"`
Ext string `json:"ext" gorm:"size:255"`
Path string `json:"path" gorm:"size:255;not null"`
Md5 string `json:"md5" gorm:"size:255;not null"`
Status int `json:"status" gorm:"size:1;not null;default:1"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// 插入数据并返回ID
func (model *Picture) InsertGetId(data map[string]interface{}) (id int, Error error) {
size := strconv.FormatInt(data["size"].(int64), 10)
picture := Picture{
ObjType: data["obj_type"].(string),
ObjId: data["obj_id"].(int),
Name: data["name"].(string),
Size: size,
Md5: data["md5"].(string),
Path: data["path"].(string),
Width: data["width"].(int),
Height: data["height"].(int),
Ext: data["ext"].(string),
Status: 1,
}
err := db.Client.Create(&picture).Error
if err != nil {
return id, err
}
return picture.Id, nil
}
// 获取图片路径
func (model *Picture) GetPath(id interface{}) string {
http, path := "", ""
webSiteDomain := (&Config{}).GetValue("WEB_SITE_DOMAIN")
WebConfig := (&Config{}).GetValue("SSL_OPEN")
if webSiteDomain != "" {
if WebConfig == "1" {
http = "https://"
} else {
http = "http://"
}
}
if getId, ok := id.(string); ok {
if strings.Contains(getId, "//") && !strings.Contains(getId, "{") {
return getId
}
if strings.Contains(getId, "./") && !strings.Contains(getId, "{") {
// 如果设置域名,则加上域名前缀
return http + webSiteDomain + strings.Replace(getId, "./storage/app/public", "/storage", -1)
}
// json字符串
if strings.Contains(getId, "{") {
var jsonData interface{}
json.Unmarshal([]byte(getId), &jsonData)
if mapData, ok := jsonData.(map[string]interface{}); ok {
path = mapData["url"].(string)
}
// 如果为数组返回第一个key的path
if arrayData, ok := jsonData.([]map[string]interface{}); ok {
path = arrayData[0]["url"].(string)
}
}
if strings.Contains(path, "//") {
return path
}
if strings.Contains(path, "./") {
path = strings.Replace(path, "./storage/app/public", "/storage", -1)
}
if path != "" {
// 如果设置域名,则加上域名前缀
return http + webSiteDomain + path
}
}
picture := &Picture{}
db.Client.Where("id", id).Where("status", 1).First(&picture)
if picture.Id != 0 {
path = picture.Path
if strings.Contains(path, "//") {
return path
}
if strings.Contains(path, "./") {
path = strings.Replace(path, "./storage/app/public", "/storage", -1)
}
}
if path != "" {
// 如果设置域名,则加上域名前缀
return http + webSiteDomain + path
}
return http + webSiteDomain + "/admin/default.png"
}

View File

@@ -0,0 +1,11 @@
package model
// 字段
type PictureCategory struct {
Id int `json:"id" gorm:"autoIncrement"`
ObjType string `json:"obj_type" gorm:"size:100"`
ObjId int `json:"obj_id" gorm:"size:11;default:0"`
Title string `json:"title" gorm:"size:255;not null"`
Sort int `json:"sort" gorm:"size:11;default:0"`
Description string `json:"description" gorm:"size:255"`
}

73
pkg/app/model/role.go Normal file
View File

@@ -0,0 +1,73 @@
package model
import (
"time"
"github.com/quarkcms/quark-go/pkg/dal/db"
)
// 角色
type Role struct {
Id int `json:"id" gorm:"autoIncrement"`
Name string `json:"name" gorm:"size:255;not null"`
GuardName string `json:"guard_name" gorm:"size:100;not null"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// 模型角色关联表
type ModelHasRole struct {
RoleId int `json:"role_id" gorm:"index:model_has_roles_model_id_model_type_index"`
ModelType string `json:"model_type" gorm:"size:255;not null"`
ModelId int `json:"model_id" gorm:"index:model_has_roles_model_id_model_type_index"`
}
// 角色权限关联表
type RoleHasPermission struct {
PermissionId int `json:"permission_id" gorm:"size:11;not null"`
RoleId int `json:"role_id" gorm:"index:role_has_permissions_role_id_foreign"`
}
// 模型权限关联表
type ModelHasPermission struct {
PermissionId int `json:"permission_id" gorm:"index:model_has_permissions_model_id_model_type_index"`
ModelType string `json:"model_type" gorm:"size:255;not null"`
ModelId int `json:"model_id" gorm:"index:model_has_permissions_model_id_model_type_index"`
}
// 获取角色列表
func (model *Role) List() (list map[interface{}]interface{}, Error error) {
roles := []Role{}
err := db.Client.Find(&roles).Error
if err != nil {
return list, err
}
for _, v := range roles {
list[v.Id] = v.Name
}
return list, nil
}
// 通过管理员ID获取角色相关
func (model *ModelHasRole) GetListByAdminId(id int) (modelHasRole *ModelHasRole, Error error) {
err := db.Client.Where("model_id", id).Where("model_type", "admin").First(&modelHasRole).Error
return modelHasRole, err
}
// 通过管理员ID获取角色id集合
func (model *ModelHasRole) GetRoleIdsByAdminId(id int) (roleIds []int, Error error) {
err := db.Client.Model(model).Where("model_id", id).Where("model_type", "admin").Pluck("id", &roleIds).Error
return roleIds, err
}
// 通过角色id集合获取权限id集合
func (model *RoleHasPermission) GetPermissionIdsByRoleIds(roleIds []int) (permissionIds []int, Error error) {
// 角色权限id
err := db.Client.Model(model).Where("role_id in (?)", roleIds).Pluck("id", &permissionIds).Error
return permissionIds, err
}

52
pkg/app/model/time.go Normal file
View File

@@ -0,0 +1,52 @@
package model
import (
"database/sql/driver"
"errors"
"fmt"
"strings"
"time"
)
//自定义时间
type Time time.Time
func (t *Time) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
return nil
}
var err error
//前端接收的时间字符串
str := string(data)
//去除接收的str收尾多余的"
timeStr := strings.Trim(str, "\"")
t1, err := time.Parse("2006-01-02 15:04:05", timeStr)
*t = Time(t1)
return err
}
func (t Time) MarshalJSON() ([]byte, error) {
formatted := fmt.Sprintf("\"%v\"", time.Time(t).Format("2006-01-02 15:04:05"))
return []byte(formatted), nil
}
func (t Time) Value() (driver.Value, error) {
// Time 转换成 time.Time 类型
tTime := time.Time(t)
return tTime.Format("2006-01-02 15:04:05"), nil
}
func (t *Time) Scan(v interface{}) error {
switch vt := v.(type) {
case time.Time:
// 字符串转成 time.Time 类型
*t = Time(vt)
case string:
// 字符串转成 time.Time 类型
getTime, _ := time.Parse("2006-01-02 15:04:05", vt)
*t = Time(getTime)
default:
return errors.New("类型处理错误")
}
return nil
}

View File

@@ -0,0 +1,633 @@
package actions
import (
"reflect"
"strings"
"github.com/gobeam/stringy"
"github.com/quarkcms/quark-go/pkg/builder"
)
type Action struct {
Name string `json:"name"`
Reload string `json:"reload"`
ActionType string `json:"actionType"`
SubmitForm string `json:"submitForm"`
Icon string `json:"icon"`
Type string `json:"type"`
Size string `json:"size"`
WithLoading bool `json:"withLoading"`
Fields interface{} `json:"fields"`
ConfirmTitle string `json:"confirmTitle"`
ConfirmText string `json:"confirmText"`
ConfirmType string `json:"confirmType"`
OnlyOnIndex bool `json:"onlyOnIndex"`
OnlyOnForm bool `json:"onlyOnForm"`
OnlyOnDetail bool `json:"onlyOnDetail"`
ShowOnIndex bool `json:"showOnIndex"`
ShowOnIndexTableRow bool `json:"showOnIndexTableRow"`
ShowOnIndexTableAlert bool `json:"showOnIndexTableAlert"`
ShowOnForm bool `json:"showOnForm"`
ShowOnFormExtra bool `json:"showOnFormExtra"`
ShowOnDetail bool `json:"showOnDetail"`
ShowOnDetailExtra bool `json:"showOnDetailExtra"`
}
// 初始化
func (p *Action) ParentInit() interface{} {
p.ActionType = "ajax"
return p
}
/**
* 行为key
*
* @return string
*/
func (p *Action) GetUriKey(action interface{}) string {
uriKey := reflect.TypeOf(action).String()
uriKey = strings.Replace(uriKey, "*actions.", "", -1)
uriKey = stringy.New(uriKey).KebabCase("?", "").ToLower()
return uriKey
}
/**
* 获取名称
*
* @return string
*/
func (p *Action) GetName() string {
return p.Name
}
/**
* 执行成功后刷新的组件
*
* @return string
*/
func (p *Action) GetReload() string {
return p.Reload
}
/**
* 行为接口接收的参数,当行为在表格行展示的时候,可以配置当前行的任意字段
*
* @return array
*/
func (p *Action) GetApiParams() []string {
return []string{}
}
/**
* 执行行为的接口
*
* @return string
*/
func (p *Action) GetApi(request *builder.Request) string {
return ""
}
/**
* 【必填】这是 action 最核心的配置,来指定该 action 的作用类型支持ajax、link、url、drawer、dialog、confirm、cancel、prev、next、copy、close。
*
* @return string
*/
func (p *Action) GetActionType() string {
return p.ActionType
}
/**
* 当action 的作用类型为submit的时候可以指定提交哪个表格submitForm为提交表单的key值为空时提交当前表单
*
* @return string
*/
func (p *Action) GetSubmitForm() string {
return p.SubmitForm
}
/**
* 设置按钮类型primary | ghost | dashed | link | text | default
*
* @return string
*/
func (p *Action) GetType() string {
return p.Type
}
/**
* 设置按钮大小,large | middle | small | default
*
* @return string
*/
func (p *Action) GetSize() string {
return p.Size
}
/**
* 是否具有loading当action 的作用类型为ajax,submit时有效
*
* @return bool
*/
func (p *Action) GetWithLoading() bool {
return p.WithLoading
}
/**
* 设置按钮的图标组件
*
* @return string
*/
func (p *Action) GetIcon() string {
return p.Icon
}
/**
* 行为表单字段
*
* @return mixed
*/
func (p *Action) GetFields() interface{} {
return p.Fields
}
/**
* 确认标题
*
* @return mixed
*/
func (p *Action) GetConfirmTitle() string {
return p.ConfirmTitle
}
/**
* 确认文字
*
* @return mixed
*/
func (p *Action) GetConfirmText() string {
return p.ConfirmText
}
/**
* 确认类型
*
* @return mixed
*/
func (p *Action) GetConfirmType() string {
return p.ConfirmType
}
/**
* 设置行为前的确认操作
*
* @param string title
* @param string text
* @param string confirmType
* @return p
*/
func (p *Action) WithConfirm(title string, text string, confirmType string) *Action {
p.ConfirmTitle = title
p.ConfirmText = text
p.ConfirmType = confirmType
return p
}
/**
* 只在列表页展示
*
* @param bool value
* @return p
*/
func (p *Action) SetOnlyOnIndex(value bool) *Action {
p.OnlyOnIndex = value
p.ShowOnIndex = value
p.ShowOnDetail = !value
p.ShowOnIndexTableRow = !value
p.ShowOnIndexTableAlert = !value
p.ShowOnForm = !value
p.ShowOnFormExtra = !value
p.ShowOnDetail = !value
p.ShowOnDetailExtra = !value
return p
}
/**
* 除了列表页外展示
*
* @return p
*/
func (p *Action) SetExceptOnIndex() *Action {
p.ShowOnDetail = true
p.ShowOnIndexTableRow = true
p.ShowOnIndexTableAlert = true
p.ShowOnForm = true
p.ShowOnFormExtra = true
p.ShowOnDetail = true
p.ShowOnDetailExtra = true
p.ShowOnIndex = false
return p
}
/**
* 只在表单页展示
*
* @param bool value
* @return p
*/
func (p *Action) SetOnlyOnForm(value bool) *Action {
p.ShowOnForm = value
p.ShowOnIndexTableAlert = !value
p.ShowOnIndex = !value
p.ShowOnDetail = !value
p.ShowOnIndexTableRow = !value
p.ShowOnFormExtra = !value
p.ShowOnDetail = !value
p.ShowOnDetailExtra = !value
return p
}
/**
* 除了表单页外展示
*
* @return p
*/
func (p *Action) SetExceptOnForm() *Action {
p.ShowOnIndexTableAlert = true
p.ShowOnIndex = true
p.ShowOnDetail = true
p.ShowOnIndexTableRow = true
p.ShowOnForm = false
p.ShowOnFormExtra = true
p.ShowOnDetail = true
p.ShowOnDetailExtra = true
return p
}
/**
* 只在表单页右上角自定义区域展示
*
* @param bool value
* @return p
*/
func (p *Action) SetOnlyOnFormExtra(value bool) *Action {
p.ShowOnForm = !value
p.ShowOnIndexTableAlert = !value
p.ShowOnIndex = !value
p.ShowOnDetail = !value
p.ShowOnIndexTableRow = !value
p.ShowOnFormExtra = value
p.ShowOnDetail = !value
p.ShowOnDetailExtra = !value
return p
}
/**
* 除了表单页右上角自定义区域外展示
*
* @return p
*/
func (p *Action) SetExceptOnFormExtra() *Action {
p.ShowOnIndexTableAlert = true
p.ShowOnIndex = true
p.ShowOnDetail = true
p.ShowOnIndexTableRow = true
p.ShowOnForm = true
p.ShowOnFormExtra = false
p.ShowOnDetail = true
p.ShowOnDetailExtra = true
return p
}
/**
* 只在详情页展示
*
* @param bool value
* @return p
*/
func (p *Action) SetOnlyOnDetail(value bool) *Action {
p.OnlyOnDetail = value
p.ShowOnDetail = value
p.ShowOnIndex = !value
p.ShowOnIndexTableRow = !value
p.ShowOnIndexTableAlert = !value
p.ShowOnForm = !value
p.ShowOnFormExtra = !value
p.ShowOnDetailExtra = !value
return p
}
/**
* 除了详情页外展示
*
* @return p
*/
func (p *Action) SetExceptOnDetail() *Action {
p.ShowOnIndex = true
p.ShowOnDetail = false
p.ShowOnIndexTableRow = true
p.ShowOnIndexTableAlert = true
p.ShowOnForm = true
p.ShowOnFormExtra = true
p.ShowOnDetailExtra = true
return p
}
/**
* 只在详情页右上角自定义区域展示
*
* @param bool value
* @return p
*/
func (p *Action) SetOnlyOnDetailExtra(value bool) *Action {
p.ShowOnForm = !value
p.ShowOnIndexTableAlert = !value
p.ShowOnIndex = !value
p.ShowOnDetail = !value
p.ShowOnIndexTableRow = !value
p.ShowOnFormExtra = !value
p.ShowOnDetail = !value
p.ShowOnDetailExtra = value
return p
}
/**
* 除了详情页右上角自定义区域外展示
*
* @return p
*/
func (p *Action) SetExceptOnDetailExtra() *Action {
p.ShowOnIndexTableAlert = true
p.ShowOnIndex = true
p.ShowOnDetail = true
p.ShowOnIndexTableRow = true
p.ShowOnForm = true
p.ShowOnFormExtra = true
p.ShowOnDetail = true
p.ShowOnDetailExtra = false
return p
}
/**
* 在表格行内展示
*
* @param bool value
* @return p
*/
func (p *Action) SetOnlyOnIndexTableRow(value bool) *Action {
p.ShowOnIndexTableRow = value
p.ShowOnIndex = !value
p.ShowOnDetail = !value
p.ShowOnIndexTableAlert = !value
p.ShowOnForm = !value
p.ShowOnFormExtra = !value
p.ShowOnDetail = !value
p.ShowOnDetailExtra = !value
return p
}
/**
* 除了表格行内外展示
*
* @return p
*/
func (p *Action) SetExceptOnIndexTableRow() *Action {
p.ShowOnIndexTableRow = false
p.ShowOnIndex = true
p.ShowOnDetail = true
p.ShowOnIndexTableAlert = true
p.ShowOnForm = true
p.ShowOnFormExtra = true
p.ShowOnDetail = true
p.ShowOnDetailExtra = true
return p
}
/**
* 在表格多选弹出层展示
*
* @param bool value
* @return p
*/
func (p *Action) SetOnlyOnIndexTableAlert(value bool) *Action {
p.ShowOnIndexTableAlert = value
p.ShowOnIndex = !value
p.ShowOnDetail = !value
p.ShowOnIndexTableRow = !value
p.ShowOnForm = !value
p.ShowOnFormExtra = !value
p.ShowOnDetail = !value
p.ShowOnDetailExtra = !value
return p
}
/**
* 除了表格多选弹出层外展示
*
* @return p
*/
func (p *Action) SetExceptOnIndexTableAlert() *Action {
p.ShowOnIndexTableAlert = false
p.ShowOnIndex = true
p.ShowOnDetail = true
p.ShowOnIndexTableRow = true
p.ShowOnForm = true
p.ShowOnFormExtra = true
p.ShowOnDetail = true
p.ShowOnDetailExtra = true
return p
}
/**
* 在列表页展示
*
* @return p
*/
func (p *Action) SetShowOnIndex() *Action {
p.ShowOnIndex = true
return p
}
/**
* 在表单页展示
*
* @return p
*/
func (p *Action) SetShowOnForm() *Action {
p.ShowOnForm = true
return p
}
/**
* 在表单页右上角自定义区域展示
*
* @return p
*/
func (p *Action) SetShowOnFormExtra() *Action {
p.ShowOnFormExtra = true
return p
}
/**
* 在详情页展示
*
* @return p
*/
func (p *Action) SetShowOnDetail() *Action {
p.ShowOnDetail = true
return p
}
/**
* 在详情页右上角自定义区域展示
*
* @return p
*/
func (p *Action) SetShowOnDetailExtra() *Action {
p.ShowOnDetailExtra = true
return p
}
/**
* 在表格行内展示
*
* @return p
*/
func (p *Action) SetShowOnIndexTableRow() *Action {
p.ShowOnIndexTableRow = true
return p
}
/**
* 在多选弹出层展示
*
* @return p
*/
func (p *Action) SetShowOnIndexTableAlert() *Action {
p.ShowOnIndexTableAlert = true
return p
}
/**
* 判断是否在列表页展示
*
* @return bool
*/
func (p *Action) ShownOnIndex() bool {
if p.OnlyOnIndex == true {
return true
}
if p.OnlyOnDetail {
return false
}
if p.OnlyOnForm {
return false
}
return p.ShowOnIndex
}
/**
* 判断是否在表单页展示
*
* @return bool
*/
func (p *Action) ShownOnForm() bool {
if p.OnlyOnForm == true {
return true
}
if p.OnlyOnDetail {
return false
}
if p.OnlyOnIndex {
return false
}
return p.ShowOnForm
}
/**
* 判断是否在详情页展示
*
* @return bool
*/
func (p *Action) ShownOnDetail() bool {
if p.OnlyOnDetail {
return true
}
if p.OnlyOnIndex {
return false
}
if p.OnlyOnForm {
return false
}
return p.ShowOnDetail
}
/**
* 判断是否在表格行内展示
*
* @return bool
*/
func (p *Action) ShownOnIndexTableRow() bool {
return p.ShowOnIndexTableRow
}
/**
* 判断是否在多选弹出层展示
*
* @return bool
*/
func (p *Action) ShownOnIndexTableAlert() bool {
return p.ShowOnIndexTableAlert
}
/**
* 判断是否在表单页右上角自定义区域展示
*
* @return bool
*/
func (p *Action) ShownOnFormExtra() bool {
return p.ShowOnFormExtra
}
/**
* 判断是否在详情页右上角自定义区域展示
*
* @return bool
*/
func (p *Action) ShownOnDetailExtra() bool {
return p.ShowOnDetailExtra
}

View File

@@ -0,0 +1,37 @@
package actions
import "github.com/quarkcms/quark-go/pkg/builder"
type Drawer struct {
Action
Width int `json:"width"`
DestroyOnClose bool `json:"destroyOnClose"`
}
// 初始化
func (p *Drawer) ParentInit() interface{} {
p.ActionType = "drawer"
p.Width = 520
return p
}
// 宽度
func (p *Drawer) GetWidth() int {
return p.Width
}
// 关闭时销毁 Modal 里的子元素
func (p *Drawer) GetDestroyOnClose() bool {
return p.DestroyOnClose
}
// 内容
func (p *Drawer) GetBody(request *builder.Request, resourceInstance interface{}) interface{} {
return nil
}
// 弹窗行为
func (p *Drawer) GetActions(request *builder.Request, resourceInstance interface{}) []interface{} {
return []interface{}{}
}

View File

@@ -0,0 +1,209 @@
package actions
import (
"strings"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/component/admin/action"
"github.com/quarkcms/quark-go/pkg/component/admin/menu"
)
type Dropdown struct {
Action
Arrow bool `json:"arrow"`
Placement string `json:"placement"`
Trigger []string `json:"trigger"`
OverlayStyle map[string]interface{} `json:"overlayStyle"`
Actions []interface{} `json:"actions"`
}
// 初始化
func (p *Dropdown) ParentInit() interface{} {
p.ActionType = "dropdown"
p.Placement = "bottomLeft"
p.Trigger = append(p.Trigger, "hover")
return p
}
// 是否显示箭头图标
func (p *Dropdown) GetArrow() bool {
return p.Arrow
}
// 菜单弹出位置bottomLeft bottomCenter bottomRight topLeft topCenter topRight
func (p *Dropdown) GetPlacement() string {
return p.Placement
}
// 触发下拉的行为, 移动端不支持 hover,Array<click|hover|contextMenu>
func (p *Dropdown) GetTrigger() []string {
return p.Trigger
}
// 下拉根元素的样式
func (p *Dropdown) GetOverlayStyle() map[string]interface{} {
return p.OverlayStyle
}
// 菜单
func (p *Dropdown) GetOverlay(request *builder.Request, templateInstance interface{}) interface{} {
actions := p.GetActions()
items := []interface{}{}
for _, v := range actions {
action := p.buildAction(request, v, templateInstance)
items = append(items, action)
}
return (&menu.Component{}).Init().SetItems(items)
}
//创建行为组件
func (p *Dropdown) buildAction(request *builder.Request, item interface{}, templateInstance interface{}) interface{} {
name := item.(interface{ GetName() string }).GetName()
withLoading := item.(interface{ GetWithLoading() bool }).GetWithLoading()
reload := item.(interface{ GetReload() string }).GetReload()
// uri唯一标识
uriKey := item.(interface {
GetUriKey(interface{}) string
}).GetUriKey(item)
// 获取api
api := item.(interface {
GetApi(*builder.Request) string
}).GetApi(request)
// 获取api替换参数
params := item.(interface {
GetApiParams() []string
}).GetApiParams()
if api == "" {
api = p.buildActionApi(request, params, uriKey)
}
actionType := item.(interface{ GetActionType() string }).GetActionType()
buttonType := item.(interface{ GetType() string }).GetType()
size := item.(interface{ GetSize() string }).GetSize()
icon := item.(interface{ GetIcon() string }).GetIcon()
confirmTitle := item.(interface{ GetConfirmTitle() string }).GetConfirmTitle()
confirmText := item.(interface{ GetConfirmText() string }).GetConfirmText()
confirmType := item.(interface{ GetConfirmType() string }).GetConfirmType()
getAction := (&menu.Item{}).Init().
Init().
SetLabel(name).
SetWithLoading(withLoading).
SetReload(reload).
SetApi(api).
SetActionType(actionType).
SetType(buttonType, false).
SetSize(size)
if icon != "" {
getAction = getAction.
SetIcon(icon)
}
switch actionType {
case "link":
href := item.(interface {
GetHref(request *builder.Request) string
}).GetHref(request)
target := item.(interface {
GetTarget(request *builder.Request) string
}).GetTarget(request)
getAction = getAction.
SetLink(href, target).
SetStyle(map[string]interface{}{
"color": "#1890ff",
})
case "modal":
formWidth := item.(interface {
GetWidth() int
}).GetWidth()
formDestroyOnClose := item.(interface {
GetDestroyOnClose() bool
}).GetDestroyOnClose()
formBody := item.(interface {
GetBody(request *builder.Request, templateInstance interface{}) interface{}
}).GetBody(request, templateInstance)
formActions := item.(interface {
GetActions(request *builder.Request, templateInstance interface{}) []interface{}
}).GetActions(request, templateInstance)
getAction = getAction.SetModal(func(modal *action.Modal) interface{} {
return modal.
SetTitle(name).
SetWidth(formWidth).
SetBody(formBody).
SetActions(formActions).
SetDestroyOnClose(formDestroyOnClose)
})
case "drawer":
formWidth := item.(interface {
GetWidth() int
}).GetWidth()
formDestroyOnClose := item.(interface {
GetDestroyOnClose() bool
}).GetDestroyOnClose()
formBody := item.(interface {
GetBody(request *builder.Request, templateInstance interface{}) interface{}
}).GetBody(request, templateInstance)
formActions := item.(interface {
GetActions(request *builder.Request, templateInstance interface{}) []interface{}
}).GetActions(request, templateInstance)
getAction = getAction.SetDrawer(func(drawer *action.Drawer) interface{} {
return drawer.
SetTitle(name).
SetWidth(formWidth).
SetBody(formBody).
SetActions(formActions).
SetDestroyOnClose(formDestroyOnClose)
})
}
if confirmTitle != "" {
getAction = getAction.
SetWithConfirm(confirmTitle, confirmText, confirmType)
}
return getAction
}
// 下拉菜单行为
func (p *Dropdown) SetActions(actions []interface{}) *Dropdown {
p.Actions = actions
return p
}
// 获取下拉菜单行为
func (p *Dropdown) GetActions() []interface{} {
return p.Actions
}
//创建行为接口
func (p *Dropdown) buildActionApi(request *builder.Request, params []string, uriKey string) string {
paramsUri := ""
for _, v := range params {
paramsUri = paramsUri + v + "=${" + v + "}&"
}
api := strings.Replace(request.Path(), "/index", "/action/"+uriKey, -1)
if paramsUri != "" {
api = api + "?" + paramsUri
}
return api
}

View File

@@ -0,0 +1,35 @@
package actions
import "github.com/quarkcms/quark-go/pkg/builder"
type Link struct {
Action
Href string `json:"href"`
Target string `json:"target"`
}
// 初始化
func (p *Link) ParentInit() interface{} {
p.ActionType = "link"
p.Target = "_self"
return p
}
/**
* 获取跳转链接
*
* @return string
*/
func (p *Link) GetHref(request *builder.Request) string {
return p.Href
}
/**
* 相当于 a 链接的 target 属性href 存在时生效
*
* @return string
*/
func (p *Link) GetTarget(request *builder.Request) string {
return p.Target
}

View File

@@ -0,0 +1,37 @@
package actions
import "github.com/quarkcms/quark-go/pkg/builder"
type Modal struct {
Action
Width int `json:"width"`
DestroyOnClose bool `json:"destroyOnClose"`
}
// 初始化
func (p *Modal) ParentInit() interface{} {
p.ActionType = "modal"
p.Width = 520
return p
}
// 宽度
func (p *Modal) GetWidth() int {
return p.Width
}
// 关闭时销毁 Modal 里的子元素
func (p *Modal) GetDestroyOnClose() bool {
return p.DestroyOnClose
}
// 内容
func (p *Modal) GetBody(request *builder.Request, templateInstance interface{}) interface{} {
return nil
}
// 弹窗行为
func (p *Modal) GetActions(request *builder.Request, templateInstance interface{}) []interface{} {
return []interface{}{}
}

894
pkg/builder/adminfield.go Normal file
View File

@@ -0,0 +1,894 @@
package builder
import (
"reflect"
"github.com/quarkcms/quark-go/pkg/component/admin/form/fields"
)
// 后台字段组件
type AdminField struct{}
// ID组件
func (p *AdminField) ID(params ...interface{}) *fields.ID {
field := (&fields.ID{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// Hidden组件
func (p *AdminField) Hidden(params ...interface{}) *fields.Hidden {
field := (&fields.Hidden{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 输入框组件
func (p *AdminField) Text(params ...interface{}) *fields.Text {
field := (&fields.Text{}).Init()
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.SetPlaceholder("请输入" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.SetPlaceholder("请输入" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 文本域组件
func (p *AdminField) TextArea(params ...interface{}) *fields.TextArea {
field := (&fields.TextArea{}).Init()
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.SetPlaceholder("请输入" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.SetPlaceholder("请输入" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 密码框组件
func (p *AdminField) Password(params ...interface{}) *fields.Password {
field := (&fields.Password{}).Init()
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.SetPlaceholder("请输入" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.SetPlaceholder("请输入" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 单选组件
func (p *AdminField) Radio(params ...string) *fields.Radio {
field := &fields.Radio{}
if len(params) == 2 {
field.Init().SetName(params[0]).SetLabel(params[1])
} else {
field.Init().SetName(params[0]).SetLabel(params[0])
}
return field
}
// 多选组件
func (p *AdminField) Checkbox(params ...string) *fields.Checkbox {
field := &fields.Checkbox{}
if len(params) == 2 {
field.Init().SetName(params[0]).SetLabel(params[1])
} else {
field.Init().SetName(params[0]).SetLabel(params[0])
}
return field
}
// 日期组件
func (p *AdminField) Date(params ...interface{}) *fields.Date {
field := &fields.Date{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 日期范围组件
func (p *AdminField) DateRange(params ...interface{}) *fields.DateRange {
field := &fields.DateRange{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 日期时间组件
func (p *AdminField) Datetime(params ...interface{}) *fields.Datetime {
field := &fields.Datetime{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 日期时间范围组件
func (p *AdminField) DatetimeRange(params ...interface{}) *fields.DatetimeRange {
field := &fields.DatetimeRange{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 开关组件
func (p *AdminField) Switch(params ...string) *fields.Switch {
field := &fields.Switch{}
if len(params) == 2 {
field.Init().SetName(params[0]).SetLabel(params[1])
} else {
field.Init().SetName(params[0]).SetLabel(params[0])
}
return field
}
// 树形组件
func (p *AdminField) Tree(params ...interface{}) *fields.Tree {
field := (&fields.Tree{}).Init()
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.SetPlaceholder("请选择" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.SetPlaceholder("请选择" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 图标组件
func (p *AdminField) Icon(params ...interface{}) *fields.Icon {
field := (&fields.Icon{}).Init()
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.SetPlaceholder("请选择" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.SetPlaceholder("请选择" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 下拉框组件
func (p *AdminField) Select(params ...interface{}) *fields.Select {
field := (&fields.Select{}).Init()
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.SetPlaceholder("请选择" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.SetPlaceholder("请选择" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 级联菜单组件
func (p *AdminField) Cascader(params ...interface{}) *fields.Select {
field := (&fields.Select{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 图片组件
func (p *AdminField) Image(params ...interface{}) *fields.Image {
field := (&fields.Image{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 文件组件
func (p *AdminField) File(params ...interface{}) *fields.File {
field := (&fields.File{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 文本展示组件
func (p *AdminField) Display(label string) *fields.Display {
field := (&fields.Display{}).Init()
field.SetLabel(label)
return field
}
// 编辑器组件
func (p *AdminField) Editor(params ...interface{}) *fields.Editor {
field := (&fields.Editor{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 分组组件
func (p *AdminField) Group(label string, items []interface{}) *fields.Group {
field := (&fields.Group{}).Init()
field.SetBody(items).SetLabel(label)
return field
}
// List组件
func (p *AdminField) List(params ...interface{}) *fields.List {
field := (&fields.List{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 地图组件
func (p *AdminField) Map(params ...interface{}) *fields.Map {
field := (&fields.Map{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 地图围栏组件
func (p *AdminField) Geofence(params ...interface{}) *fields.Geofence {
field := (&fields.Geofence{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 日期-月组件
func (p *AdminField) Month(params ...interface{}) *fields.Month {
field := &fields.Month{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 数组输入框组件
func (p *AdminField) Number(params ...interface{}) *fields.Number {
field := (&fields.Number{}).Init()
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.SetPlaceholder("请输入" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.SetPlaceholder("请输入" + params[1].(string))
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 日期-季度组件
func (p *AdminField) Quarter(params ...interface{}) *fields.Quarter {
field := &fields.Quarter{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 搜索组件
func (p *AdminField) Search(params ...interface{}) *fields.Search {
field := (&fields.Search{}).Init()
if len(params) >= 2 {
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 时间范围组件
func (p *AdminField) TimeRange(params ...interface{}) *fields.TimeRange {
field := &fields.TimeRange{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 时间组件
func (p *AdminField) Time(params ...interface{}) *fields.Time {
field := &fields.Time{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 周组件
func (p *AdminField) Week(params ...interface{}) *fields.Week {
field := &fields.Week{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// 年组件
func (p *AdminField) Year(params ...interface{}) *fields.Year {
field := &fields.Year{}
placeholder := reflect.
ValueOf(field).
Elem().
FieldByName("Placeholder").String()
if len(params) >= 2 {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[1].(string))
if len(params) == 3 {
// 判断是否为闭包函数
closure, ok := params[2].(func() interface{})
if ok {
field.SetCallback(closure)
}
}
} else {
if placeholder == "" {
field.Init().SetPlaceholder("请选择")
}
field.SetName(params[0].(string)).SetLabel(params[0].(string))
}
return field
}
// Select组合组件
func (p *AdminField) Selects(body interface{}) *fields.Selects {
field := &fields.Selects{}
field.Init().SetBody(body)
return field
}

232
pkg/builder/builder.go Normal file
View File

@@ -0,0 +1,232 @@
package builder
import (
"reflect"
"strings"
"time"
"github.com/jinzhu/copier"
"github.com/quarkcms/quark-go/pkg/dal"
"github.com/quarkcms/quark-go/pkg/github"
"gorm.io/gorm"
)
// 静态文件URL
const RespositoryURL = "https://github.com/quarkcms/quark-go/tree/2.0/website/"
type DBConfig struct {
Dialector gorm.Dialector
Opts gorm.Option
}
type Config struct {
AppKey string // 应用加密Key用于JWT认证
AppName string // 应用名称
DBConfig *DBConfig // 数据库配置
StaticPath string // 静态文件目录
Providers []interface{} // 服务列表
AdminLayout *AdminLayout // 后台布局
Routes []string // 路由列表
}
// 全局配置
var AppConfig *Config
// 初始化对象
func New(config *Config) *Resource {
// 初始化数据库
if config.DBConfig != nil {
dal.InitDB(config.DBConfig.Dialector, config.DBConfig.Opts)
}
// 初始化配置
SetConfig(config)
// 定义结构体
resource := &Resource{
Providers: AppConfig.Providers,
}
// 下载静态文件
github.Download(RespositoryURL, config.StaticPath)
// 调用初始化方法
return resource
}
// 转换Request对象
func (p *Resource) TransformRequest(request *Request) *Resource {
requestInstance := request.Init()
// 定义结构体
resource := &Resource{
Providers: p.Providers,
UseHandlers: p.UseHandlers,
Request: requestInstance,
}
// 初始化路由列表
p.InitRoutes()
// 调用初始化方法
return resource
}
// 初始化路由列表
func (p *Resource) InitRoutes() {
if AppConfig.Routes != nil {
return
}
var routes []string
for _, provider := range AppConfig.Providers {
// 初始化
getTemplateInstance := provider.(interface {
Init() interface{}
}).Init()
// 获取模板定义的路由
templateInstanceRoutes := getTemplateInstance.(interface {
GetRoutes() []*Route
}).GetRoutes()
for _, v := range templateInstanceRoutes {
providerName := reflect.TypeOf(provider).String()
getNames := strings.Split(providerName, ".")
structName := getNames[len(getNames)-1]
if strings.Contains(v.Path, ":resource") {
path := strings.Replace(v.Path, ":resource", strings.ToLower(structName), -1)
//处理行为
if strings.Contains(path, ":uriKey") {
actions := getTemplateInstance.(interface {
Actions(request *Request) []interface{}
}).Actions(p.Request)
for _, av := range actions {
// uri唯一标识
uriKey := av.(interface {
GetUriKey(interface{}) string
}).GetUriKey(av)
actionType := av.(interface{ GetActionType() string }).GetActionType()
if actionType == "dropdown" {
dropdownActions := av.(interface{ GetActions() []interface{} }).GetActions()
for _, dropdownAction := range dropdownActions {
uriKey := dropdownAction.(interface {
GetUriKey(interface{}) string
}).GetUriKey(dropdownAction) // uri唯一标识
path = strings.Replace(path, ":uriKey", uriKey, -1)
}
} else {
path = strings.Replace(path, ":uriKey", uriKey, -1)
}
}
}
routes = append(routes, path)
}
}
}
AppConfig.Routes = routes
}
// 获取后台默认布局
func getDefaultAdminLayout(adminLayout *AdminLayout) *AdminLayout {
defalutAdminLayout := &AdminLayout{
"QuarkGo",
false,
[]map[string]interface{}{
{
"component": "icon",
"icon": "icon-question-circle",
"tooltip": "使用文档",
"href": "https://www.quarkcms.com/",
"target": "_blank",
"style": map[string]interface{}{
"color": "#000",
},
},
},
"side",
false,
"dark",
"Fluid",
"dark",
"#1890ff",
true,
true,
"//at.alicdn.com/t/font_1615691_3pgkh5uyob.js",
"zh-CN",
208,
time.Now().Format("2006") + " QuarkGo",
[]map[string]interface{}{
{
"title": "Quark",
"href": "http://www.quarkcms.com/",
},
{
"title": "爱小圈",
"href": "http://www.ixiaoquan.com",
},
{
"title": "Github",
"href": "https://github.com/quarkcms",
},
},
}
// 设置布局
copier.CopyWithOption(defalutAdminLayout, adminLayout, copier.Option{IgnoreEmpty: true})
return defalutAdminLayout
}
// 设置配置
func SetConfig(config *Config) {
// 初始化后台布局
config.AdminLayout = getDefaultAdminLayout(config.AdminLayout)
// 赋给全局变量
AppConfig = config
}
// 获取当前配置
func GetConfig() *Config {
return AppConfig
}
// 获取当前AdminLayout配置
func GetAdminLayoutConfig() *AdminLayout {
return AppConfig.AdminLayout
}
// 获取路由列表
func GetRoutes() []string {
return AppConfig.Routes
}
// 通用调用方法
func (p *Resource) Use(args interface{}) *Resource {
argsName := reflect.TypeOf(args).String()
switch argsName {
case "*builder.AdminLayout":
AppConfig.AdminLayout = getDefaultAdminLayout(args.(*AdminLayout))
case "func(*builder.Request) error":
p.UseHandlers = append(p.UseHandlers, args.(func(request *Request) error))
default:
panic(argsName + " arguments was not found")
}
return p
}

View File

@@ -0,0 +1,14 @@
package metrics
import (
"github.com/quarkcms/quark-go/pkg/component/admin/descriptions"
)
type AdminDescriptions struct {
AdminMetrics
}
// 包含组件的结果
func (p *AdminDescriptions) Result(value interface{}) *descriptions.Component {
return (&descriptions.Component{}).Init().SetTitle(p.Title).SetItems(value)
}

View File

@@ -0,0 +1,6 @@
package metrics
type AdminMetrics struct {
Title string
Col int
}

View File

@@ -0,0 +1,24 @@
package metrics
import (
"github.com/quarkcms/quark-go/pkg/component/admin/statistic"
"gorm.io/gorm"
)
type AdminValue struct {
AdminMetrics
Precision int
}
// 记录条数
func (p *AdminValue) Count(DB *gorm.DB) *statistic.Component {
var count int64
DB.Count(&count)
return p.Result(count)
}
// 包含组件的结果
func (p *AdminValue) Result(value int64) *statistic.Component {
return (&statistic.Component{}).Init().SetTitle(p.Title).SetValue(value)
}

229
pkg/builder/request.go Normal file
View File

@@ -0,0 +1,229 @@
package builder
import (
"encoding/json"
"strings"
"github.com/derekstavis/go-qs"
"github.com/gobeam/stringy"
)
type Request struct {
IPString string `json:"IPString"` // 请求的ip地址
HeaderString string `json:"headerString"` // 请求的Header字符串
MethodString string `json:"methodString"` // 请求方法
FullPathString string `json:"fullPathString"` // 路由
HostString string `json:"hostString"` // 主机地址
PathString string `json:"pathString"` // URL路径
QueryString string `json:"queryString"` // 请求参数
Headers map[string]string `json:"headers"` // 请求的Headers
Params map[string]string `json:"params"` // URL param
Querys map[string]interface{} `json:"querys"` // URL querys
BodyBuffer []byte `json:"bodyBuffer"` // 请求的Body数据
}
type ParamValue struct {
Key int
Value string
}
// 初始化
func (p *Request) Init() *Request {
p.parseParams()
p.parseHeaders()
p.parseQuerys()
return p
}
// Method return request method.
//
// Returned value is valid until returning from RequestHandler.
func (p *Request) Method() string {
return p.MethodString
}
// FullPath returns a matched route full path. For not found routes
// returns an empty string.
//
// router.GET("/user/:id", func(c *hertz.RequestContext) {
// c.FullPath() == "/user/:id" // true
// })
func (p *Request) FullPath() string {
return p.FullPathString
}
// Host returns requested host.
//
// The host is valid until returning from RequestHandler.
func (p *Request) Host() string {
return p.HostString
}
// Path returns requested path.
//
// The path is valid until returning from RequestHandler.
func (p *Request) Path() string {
return p.PathString
}
// IP tries to parse the headers in [X-Real-Ip, X-Forwarded-For]. It calls RemoteIP() under the hood
func (p *Request) IP() string {
return p.IPString
}
// OriginalURL returns url query data
func (p *Request) OriginalURL() string {
return p.QueryString
}
// Body returns body data
func (p *Request) Body() []byte {
return p.BodyBuffer
}
// Param returns the value of the URL param.
// It is a shortcut for c.Params.ByName(key)
func (p *Request) parseParams() {
params := map[string]string{}
fullPaths := strings.Split(p.FullPath(), "/")
paramValues := []*ParamValue{}
for k, v := range fullPaths {
if strings.Contains(v, ":") {
v = stringy.New(v).ReplaceFirst(":", "")
mapValue := &ParamValue{
Key: k,
Value: v,
}
paramValues = append(paramValues, mapValue)
}
}
paths := strings.Split(p.Path(), "/")
for _, v := range paramValues {
params[v.Value] = paths[v.Key]
}
p.Params = params
}
// Param returns the value of the URL param.
// It is a shortcut for c.Params.ByName(key)
//
// router.GET("/user/:id", func(c *hertz.RequestContext) {
// // a GET request to /user/john
// id := c.Param("id") // id == "john"
// })
func (p *Request) Param(key string) string {
return p.Params[key]
}
// Query returns the value of the URL Querys.
func (p *Request) parseQuerys() {
querys, err := qs.Unmarshal(p.QueryString)
if err == nil {
p.Querys = querys
}
}
// Query returns the value of the URL query.
func (p *Request) Query(params ...interface{}) interface{} {
if len(params) == 2 {
if p.Querys[params[0].(string)] == nil {
return params[1]
}
}
return p.Querys[params[0].(string)]
}
// AllQuerys returns all query arguments from RequestURI.
func (p *Request) AllQuerys() map[string]interface{} {
return p.Querys
}
// ResourceName returns the value of the URL Resource param.
// If request path is "/api/admin/login/index" and route is "/api/admin/login/:resource"
//
// resourceName := p.ResourceName() // resourceName = "index"
func (p *Request) ResourceName() string {
return p.Param("resource")
}
// BodyParser binds the request body to a struct.
// It supports decoding the following content types based on the Content-Type header:
// application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data
// If none of the content types above are matched, it will return a ErrUnprocessableEntity error
func (p *Request) BodyParser(out interface{}) error {
return json.Unmarshal(p.BodyBuffer, out)
}
// 解析头部数据
func (p *Request) parseHeaders() {
params := map[string]string{}
headers := strings.Split(p.HeaderString, "\r\n")
for _, v := range headers {
header := strings.Split(v, ": ")
if len(header) == 2 {
params[header[0]] = header[1]
}
}
p.Headers = params
}
// 获取请求头数据
func (p *Request) Header(key string) string {
return p.Headers[key]
}
// 获取Header中的token
func (p *Request) Token() string {
authorization := p.Header("Authorization")
authorizations := strings.Split(authorization, " ")
if len(authorizations) == 2 {
return authorizations[1]
}
queryToken := p.Query("token", "")
if queryToken.(string) != "" {
return queryToken.(string)
}
return ""
}
// 判断当前页面是否为列表页面 todo
func (p *Request) IsIndex() bool {
uri := strings.Split(p.Path(), "/")
return (uri[len(uri)-1] == "index")
}
//判断当前页面是否为创建页面
func (p *Request) IsCreating() bool {
uri := strings.Split(p.Path(), "/")
return (uri[len(uri)-1] == "create") || (uri[len(uri)-1] == "store")
}
// 判断当前页面是否为编辑页面
func (p *Request) IsEditing() bool {
uri := strings.Split(p.Path(), "/")
return (uri[len(uri)-1] == "edit") || (uri[len(uri)-1] == "update")
}
// 判断当前页面是否为详情页面
func (p *Request) IsDetail() bool {
uri := strings.Split(p.Path(), "/")
return (uri[len(uri)-1] == "detail")
}
// 判断当前页面是否为导出页面
func (p *Request) isExport() bool {
uri := strings.Split(p.Path(), "/")
return (uri[len(uri)-1] == "export")
}

169
pkg/builder/resource.go Normal file
View File

@@ -0,0 +1,169 @@
package builder
import (
"errors"
"reflect"
"strings"
)
type Route struct {
Path string
HandlerName string
}
type AdminLayout struct {
Title string // layout 的左上角 的 title
Logo interface{} // layout 的左上角 的 logo
HeaderActions []map[string]interface{} // layout 的头部行为
Layout string // layout 的菜单模式,side右侧导航top顶部导航mix混合模式
SplitMenus bool // layout 的菜单模式为mix时是否自动分割菜单
HeaderTheme string // layout 的菜单模式为mix时顶部主题 'dark' | 'light'
ContentWidth string // layout 的内容模式,Fluid定宽 1200pxFixed自适应
NavTheme string // 导航的主题,'light' | 'dark'
PrimaryColor string // 主题色,"#1890ff"
FixedHeader bool // 是否固定 header 到顶部
FixSiderbar bool // 是否固定导航
IconfontUrl string // 使用 IconFont 的图标配置
Locale string // 当前 layout 的语言设置,'zh-CN' | 'zh-TW' | 'en-US'
SiderWidth int // 侧边菜单宽度
Copyright string // 网站版权 time.Now().Format("2006") + " QuarkGo"
Links []map[string]interface{} // 友情链接
}
type Resource struct {
Providers []interface{} // 服务列表
Request *Request // 请求数据
TemplateInstance interface{} // 资源模板实例
UseHandlers []func(request *Request) error // 中间件方法
}
// 解析UseHandler方法
func (p *Resource) UseHandlerParser() error {
var err error
// 执行本资源的方法
for _, Handler := range p.UseHandlers {
err = Handler(p.Request)
if err != nil {
return err
}
}
return err
}
// 解析路由方法
func (p *Resource) RouteParser() (interface{}, error) {
var (
result interface{}
err error
templateInstance interface{}
)
// 获取模板实例
templateInstance, err = p.GetTemplateInstance()
if err != nil {
return nil, err
}
// 设置模板实例
p.SetTemplateInstance(templateInstance)
// 执行挂载的方法
templateInstanceRoutes := templateInstance.(interface {
GetRoutes() []*Route
}).GetRoutes()
for _, v := range templateInstanceRoutes {
if v.Path == p.Request.FullPath() {
handlerResult := reflect.
ValueOf(templateInstance).
MethodByName(v.HandlerName).
Call([]reflect.Value{
reflect.ValueOf(p.Request),
reflect.ValueOf(p),
reflect.ValueOf(templateInstance),
})
if len(handlerResult) == 1 {
result = handlerResult[0].Interface()
}
}
}
return result, err
}
// 替换路由中的资源参数
//
// url := p.RouteToResourceUrl("/api/admin/login/:resource/captchaId") // url = "/api/admin/login/index/captchaId"
func (p *Resource) RouteToResourceUrl(route string) string {
resourceName := p.Request.ResourceName()
return strings.ReplaceAll(route, ":resource", resourceName)
}
// 根据路由判断是否为当前加载实例
func (p *Resource) IsCurrentTemplateInstance(provider interface{}) bool {
providerName := reflect.TypeOf(provider).String()
getNames := strings.Split(providerName, ".")
structName := getNames[len(getNames)-1]
resourceName := p.Request.ResourceName()
// fmt.Println(providerName)
// fmt.Println(resourceName)
return strings.EqualFold(strings.ToLower(structName), strings.ToLower(resourceName))
}
// 获取当前模板实例
func (p *Resource) GetTemplateInstance() (interface{}, error) {
var templateInstance interface{}
for _, provider := range p.Providers {
// 初始化
getTemplateInstance := provider.(interface {
Init() interface{}
}).Init()
// 获取模板定义的路由
templateInstanceRoutes := getTemplateInstance.(interface {
GetRoutes() []*Route
}).GetRoutes()
for _, v := range templateInstanceRoutes {
if v.Path == p.Request.FullPath() {
if p.IsCurrentTemplateInstance(provider) {
// 设置实例
templateInstance = getTemplateInstance
}
}
}
}
if templateInstance == nil {
return nil, errors.New("未获取到实例")
}
return templateInstance, nil
}
// 设置当前模板实例
func (p *Resource) SetTemplateInstance(templateInstance interface{}) {
// 设置实例
p.TemplateInstance = templateInstance
}
// 处理执行
func (p *Resource) Run() (interface{}, error) {
// 解析UseHandler方法
err := p.UseHandlerParser()
if err != nil {
return nil, err
}
// 解析路由
return p.RouteParser()
}

View File

@@ -0,0 +1,12 @@
package searches
type Date struct {
Search
}
// 初始化
func (p *Date) ParentInit() interface{} {
p.Component = "date"
return p
}

View File

@@ -0,0 +1,13 @@
package searches
type DateRange struct {
Search
}
// 初始化
func (p *DateRange) ParentInit() interface{} {
p.Component = "date"
p.Operator = "between"
return p
}

View File

@@ -0,0 +1,12 @@
package searches
type Datetime struct {
Search
}
// 初始化
func (p *Datetime) ParentInit() interface{} {
p.Component = "datetime"
return p
}

View File

@@ -0,0 +1,13 @@
package searches
type DatetimeRange struct {
Search
}
// 初始化
func (p *DatetimeRange) ParentInit() interface{} {
p.Component = "datetime"
p.Operator = "between"
return p
}

View File

@@ -0,0 +1,101 @@
package searches
import (
"reflect"
"strings"
"github.com/gobeam/stringy"
"github.com/quarkcms/quark-go/pkg/builder"
"gorm.io/gorm"
)
type Search struct {
Column string `json:"column"`
Name string `json:"name"`
Component string `json:"component"`
Operator string `json:"operator"`
Api string `json:"api"`
}
// 初始化
func (p *Search) ParentInit() interface{} {
p.Component = "input"
return p
}
/**
* 获取字段名
*
* @return string
*/
func (p *Search) GetColumn(search interface{}) string {
if p.Column == "" {
column := reflect.TypeOf(search).String()
column = strings.Replace(column, "*searches.", "", -1)
return stringy.New(column).ToLower()
}
return p.Column
}
/**
* 获取名称
*
* @return string
*/
func (p *Search) GetName() string {
return p.Name
}
/**
* 获取组件名称
*
* @return string
*/
func (p *Search) GetComponent() string {
return p.Component
}
/**
* 获取接口
*
* @return string
*/
func (p *Search) GetApi() string {
return p.Api
}
/**
* 获取操作符
*
* @return string
*/
func (p *Search) GetOperator() string {
return p.Operator
}
/**
* 默认值
*
* @var string
*/
func (p *Search) GetDefault() interface{} {
return true
}
// 执行查询
func (p *Search) Apply(request *builder.Request, query *gorm.DB, value interface{}) *gorm.DB {
return query
}
// 属性
func (p *Search) Options(request *builder.Request) map[interface{}]interface{} {
return nil
}
// 单向联动,返回数据类型map[string]string{"field": "you_want_load_field","api": "admin/resource_name/action/select-options"}
func (p *Search) Load(request *builder.Request) map[string]string {
return nil
}

View File

@@ -0,0 +1,12 @@
package searches
type Select struct {
Search
}
// 初始化
func (p *Select) ParentInit() interface{} {
p.Component = "select"
return p
}

View File

@@ -0,0 +1,110 @@
package admindashboard
import (
"reflect"
"github.com/quarkcms/quark-go/pkg/builder"
"github.com/quarkcms/quark-go/pkg/builder/template"
"github.com/quarkcms/quark-go/pkg/component/admin/card"
"github.com/quarkcms/quark-go/pkg/component/admin/descriptions"
"github.com/quarkcms/quark-go/pkg/component/admin/grid"
"github.com/quarkcms/quark-go/pkg/component/admin/statistic"
"github.com/quarkcms/quark-go/pkg/dal/db"
"github.com/quarkcms/quark-go/pkg/msg"
)
// 后台登录模板
type Template struct {
template.AdminTemplate
Title string // 标题
SubTitle string // 子标题
}
// 初始化
func (p *Template) Init() interface{} {
p.TemplateInit()
return p
}
// 初始化模板
func (p *Template) TemplateInit() interface{} {
// 初始化数据对象
p.DB = db.Client
// 清空路由
p.Routes = nil
// 注册路由
p.AddRoute("/api/admin/dashboard/:resource/index", "Render") // 后台仪表盘路由
// 标题
p.Title = "仪表盘"
return p
}
// 内容
func (p *Template) Cards(request *builder.Request) interface{} {
return nil
}
// 组件渲染
func (p *Template) Render(request *builder.Request, resource *builder.Resource, templateInstance interface{}) interface{} {
cards := templateInstance.(interface {
Cards(*builder.Request) interface{}
}).Cards(request)
if cards == nil {
return msg.Error("请实现Cards内容", "")
}
var cols []interface{}
var body []interface{}
var colNum int = 0
for key, v := range cards.([]interface{}) {
// 断言statistic组件类型
statistic, ok := v.(interface{ Calculate() *statistic.Component })
item := (&card.Component{}).Init()
if ok {
item = item.SetBody(statistic.Calculate())
} else {
// 断言descriptions组件类型
descriptions, ok := v.(interface {
Calculate() *descriptions.Component
})
if ok {
item = item.SetBody(descriptions.Calculate())
}
}
col := reflect.
ValueOf(v).
Elem().
FieldByName("Col").Int()
colInfo := (&grid.Col{}).Init().SetSpan(int(col)).SetBody(item)
cols = append(cols, colInfo)
colNum = colNum + int(col)
if colNum%24 == 0 {
row := (&grid.Row{}).Init().SetGutter(8).SetBody(cols)
if key != 1 {
row = row.SetStyle(map[string]interface{}{"marginTop": "20px"})
}
body = append(body, row)
cols = nil
}
}
if cols != nil {
row := (&grid.Row{}).Init().SetGutter(8).SetBody(cols)
if colNum > 24 {
row = row.SetStyle(map[string]interface{}{"marginTop": "20px"})
}
body = append(body, row)
}
return templateInstance.(interface {
PageComponentRender(request *builder.Request, templateInstance interface{}, body interface{}) interface{}
}).PageComponentRender(request, templateInstance, body)
}

Some files were not shown because too many files have changed in this diff Show More