mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-04 05:00:25 +08:00
阶段性提交
This commit is contained in:
44
internal/web/actions/actionutils/menu.go
Normal file
44
internal/web/actions/actionutils/menu.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package actionutils
|
||||
|
||||
// 子菜单定义
|
||||
type Menu struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Items []*MenuItem `json:"items"`
|
||||
IsActive bool `json:"isActive"`
|
||||
AlwaysActive bool `json:"alwaysActive"`
|
||||
Index int `json:"index"`
|
||||
CountNormalItems int `json:"countNormalItems"`
|
||||
}
|
||||
|
||||
// 获取新对象
|
||||
func NewMenu() *Menu {
|
||||
return &Menu{
|
||||
Items: []*MenuItem{},
|
||||
}
|
||||
}
|
||||
|
||||
// 添加菜单项
|
||||
func (this *Menu) Add(name string, subName string, url string, isActive bool) *MenuItem {
|
||||
item := &MenuItem{
|
||||
Name: name,
|
||||
SubName: subName,
|
||||
URL: url,
|
||||
IsActive: isActive,
|
||||
}
|
||||
this.CountNormalItems++
|
||||
this.Items = append(this.Items, item)
|
||||
|
||||
if isActive {
|
||||
this.IsActive = true
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
// 添加特殊菜单项,不计数
|
||||
func (this *Menu) AddSpecial(name string, subName string, url string, isActive bool) *MenuItem {
|
||||
item := this.Add(name, subName, url, isActive)
|
||||
this.CountNormalItems--
|
||||
return item
|
||||
}
|
||||
48
internal/web/actions/actionutils/menu_group.go
Normal file
48
internal/web/actions/actionutils/menu_group.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package actionutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
)
|
||||
|
||||
// 菜单分组
|
||||
type MenuGroup struct {
|
||||
Menus []*Menu `json:"menus"`
|
||||
AlwaysMenu *Menu `json:"alwaysMenu"`
|
||||
}
|
||||
|
||||
// 获取新菜单分组对象
|
||||
func NewMenuGroup() *MenuGroup {
|
||||
return &MenuGroup{
|
||||
Menus: []*Menu{},
|
||||
}
|
||||
}
|
||||
|
||||
// 查找菜单,如果找不到则自动创建
|
||||
func (this *MenuGroup) FindMenu(menuId string, menuName string) *Menu {
|
||||
for _, m := range this.Menus {
|
||||
if m.Id == menuId {
|
||||
return m
|
||||
}
|
||||
}
|
||||
menu := NewMenu()
|
||||
menu.Id = menuId
|
||||
menu.Name = menuName
|
||||
menu.Items = []*MenuItem{}
|
||||
this.Menus = append(this.Menus, menu)
|
||||
return menu
|
||||
}
|
||||
|
||||
// 排序
|
||||
func (this *MenuGroup) Sort() {
|
||||
lists.Sort(this.Menus, func(i int, j int) bool {
|
||||
menu1 := this.Menus[i]
|
||||
menu2 := this.Menus[j]
|
||||
return menu1.Index < menu2.Index
|
||||
})
|
||||
}
|
||||
|
||||
// 设置子菜单
|
||||
func SetSubMenu(action actions.ActionWrapper, menu *MenuGroup) {
|
||||
action.Object().Data["teaSubMenus"] = menu
|
||||
}
|
||||
14
internal/web/actions/actionutils/menu_item.go
Normal file
14
internal/web/actions/actionutils/menu_item.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package actionutils
|
||||
|
||||
// 菜单项
|
||||
type MenuItem struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
SubName string `json:"subName"` // 副标题
|
||||
SupName string `json:"supName"` // 头标
|
||||
URL string `json:"url"`
|
||||
IsActive bool `json:"isActive"`
|
||||
Icon string `json:"icon"`
|
||||
IsSortable bool `json:"isSortable"`
|
||||
SubColor string `json:"subColor"`
|
||||
}
|
||||
161
internal/web/actions/actionutils/page.go
Normal file
161
internal/web/actions/actionutils/page.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package actionutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"math"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Page struct {
|
||||
Offset int // 开始位置
|
||||
Size int // 每页显示数量
|
||||
Current int // 当前页码
|
||||
Max int // 最大页码
|
||||
Total int // 总数量
|
||||
|
||||
Path string
|
||||
Query url.Values
|
||||
}
|
||||
|
||||
func NewActionPage(actionPtr actions.ActionWrapper, total int, size int) *Page {
|
||||
action := actionPtr.Object()
|
||||
currentPage := action.ParamInt("page")
|
||||
|
||||
paramSize := action.ParamInt("pageSize")
|
||||
if paramSize > 0 {
|
||||
size = paramSize
|
||||
}
|
||||
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
|
||||
page := &Page{
|
||||
Current: currentPage,
|
||||
Total: total,
|
||||
Size: size,
|
||||
Path: action.Request.URL.Path,
|
||||
Query: action.Request.URL.Query(),
|
||||
}
|
||||
page.calculate()
|
||||
return page
|
||||
}
|
||||
|
||||
func (this *Page) calculate() {
|
||||
if this.Current < 1 {
|
||||
this.Current = 1
|
||||
}
|
||||
if this.Size <= 0 {
|
||||
this.Size = 10
|
||||
}
|
||||
|
||||
this.Offset = this.Size * (this.Current - 1)
|
||||
this.Max = int(math.Ceil(float64(this.Total) / float64(this.Size)))
|
||||
}
|
||||
|
||||
func (this *Page) AsHTML() string {
|
||||
if this.Total <= this.Size {
|
||||
return ""
|
||||
}
|
||||
|
||||
result := []string{}
|
||||
|
||||
// 首页
|
||||
if this.Max > 0 {
|
||||
result = append(result, `<a href="`+this.composeURL(1)+`">首页</a>`)
|
||||
} else {
|
||||
result = append(result, `<a>首页</a>`)
|
||||
}
|
||||
|
||||
// 上一页
|
||||
if this.Current <= 1 {
|
||||
result = append(result, `<a>上一页</a>`)
|
||||
} else {
|
||||
result = append(result, `<a href="`+this.composeURL(this.Current-1)+`">上一页</a>`)
|
||||
}
|
||||
|
||||
// 中间页数
|
||||
before5 := this.max(this.Current-5, 1)
|
||||
after5 := this.min(before5+9, this.Max)
|
||||
|
||||
if before5 > 1 {
|
||||
result = append(result, `<a>...</a>`)
|
||||
}
|
||||
|
||||
for i := before5; i <= after5; i++ {
|
||||
if i == this.Current {
|
||||
result = append(result, `<a href="`+this.composeURL(i)+`" class="active">`+strconv.Itoa(i)+`</a>`)
|
||||
} else {
|
||||
result = append(result, `<a href="`+this.composeURL(i)+`">`+strconv.Itoa(i)+`</a>`)
|
||||
}
|
||||
}
|
||||
|
||||
if after5 < this.Max {
|
||||
result = append(result, `<a>...</a>`)
|
||||
}
|
||||
|
||||
// 下一页
|
||||
if this.Current >= this.Max {
|
||||
result = append(result, "<a>下一页</a>")
|
||||
} else {
|
||||
result = append(result, `<a href="`+this.composeURL(this.Current+1)+`">下一页</a>`)
|
||||
}
|
||||
|
||||
// 尾页
|
||||
if this.Max > 0 {
|
||||
result = append(result, `<a href="`+this.composeURL(this.Max)+`">尾页</a>`)
|
||||
} else {
|
||||
result = append(result, `<a>尾页</a>`)
|
||||
}
|
||||
|
||||
// 每页数
|
||||
result = append(result, `<select class="ui dropdown" style="height:34px;padding-top:0;padding-bottom:0;margin-left:1em;color:#666" onchange="ChangePageSize(this.value)">
|
||||
<option value="10">[每页]</option>`+this.renderSizeOption(10)+
|
||||
this.renderSizeOption(20)+
|
||||
this.renderSizeOption(30)+
|
||||
this.renderSizeOption(40)+
|
||||
this.renderSizeOption(50)+
|
||||
this.renderSizeOption(60)+
|
||||
this.renderSizeOption(70)+
|
||||
this.renderSizeOption(80)+
|
||||
this.renderSizeOption(90)+
|
||||
this.renderSizeOption(100)+`
|
||||
</select>`)
|
||||
|
||||
return `<div class="page">` + strings.Join(result, "") + `</div>`
|
||||
}
|
||||
|
||||
// 判断是否为最后一页
|
||||
func (this *Page) IsLastPage() bool {
|
||||
return this.Current == this.Max
|
||||
}
|
||||
|
||||
func (this *Page) composeURL(page int) string {
|
||||
this.Query["page"] = []string{strconv.Itoa(page)}
|
||||
return this.Path + "?" + this.Query.Encode()
|
||||
}
|
||||
|
||||
func (this *Page) min(i, j int) int {
|
||||
if i < j {
|
||||
return i
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
func (this *Page) max(i, j int) int {
|
||||
if i < j {
|
||||
return j
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (this *Page) renderSizeOption(size int) string {
|
||||
o := `<option value="` + strconv.Itoa(size) + `"`
|
||||
if size == this.Size {
|
||||
o += ` selected="selected"`
|
||||
}
|
||||
o += `>` + strconv.Itoa(size) + `条</option>`
|
||||
return o
|
||||
}
|
||||
40
internal/web/actions/actionutils/page_test.go
Normal file
40
internal/web/actions/actionutils/page_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package actionutils
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewActionPage(t *testing.T) {
|
||||
page := &Page{
|
||||
Current: 3,
|
||||
Total: 105,
|
||||
Size: 20,
|
||||
Path: "/hello",
|
||||
Query: url.Values{
|
||||
"a": []string{"b"},
|
||||
"c": []string{"d"},
|
||||
"page": []string{"3"},
|
||||
},
|
||||
}
|
||||
page.calculate()
|
||||
t.Log(page.AsHTML())
|
||||
//logs.PrintAsJSON(page, t)
|
||||
}
|
||||
|
||||
func TestNewActionPage2(t *testing.T) {
|
||||
page := &Page{
|
||||
Current: 3,
|
||||
Total: 105,
|
||||
Size: 10,
|
||||
Path: "/hello",
|
||||
Query: url.Values{
|
||||
"a": []string{"b"},
|
||||
"c": []string{"d"},
|
||||
"page": []string{"3"},
|
||||
},
|
||||
}
|
||||
page.calculate()
|
||||
t.Log(page.AsHTML())
|
||||
//logs.PrintAsJSON(page, t)
|
||||
}
|
||||
78
internal/web/actions/actionutils/parent_action.go
Normal file
78
internal/web/actions/actionutils/parent_action.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package actionutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc/admin"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ParentAction struct {
|
||||
actions.ActionObject
|
||||
}
|
||||
|
||||
func (this *ParentAction) ErrorPage(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 日志
|
||||
this.CreateLog(oplogs.LevelError, "系统发生错误:%s", err.Error())
|
||||
|
||||
if this.Request.Method == http.MethodGet {
|
||||
FailPage(this, err)
|
||||
} else {
|
||||
Fail(this, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ParentAction) ErrorText(err string) {
|
||||
this.ErrorPage(errors.New(err))
|
||||
}
|
||||
|
||||
func (this *ParentAction) NotFound(name string, itemId int) {
|
||||
this.ErrorPage(errors.New(name + " id: '" + strconv.Itoa(itemId) + "' is not found"))
|
||||
}
|
||||
|
||||
func (this *ParentAction) NewPage(total int, size ...int) *Page {
|
||||
if len(size) > 0 {
|
||||
return NewActionPage(this, total, size[0])
|
||||
}
|
||||
return NewActionPage(this, total, 10)
|
||||
}
|
||||
|
||||
func (this *ParentAction) Nav(mainMenu string, tab string, firstMenu string) {
|
||||
this.Data["mainMenu"] = mainMenu
|
||||
this.Data["mainTab"] = tab
|
||||
this.Data["firstMenuItem"] = firstMenu
|
||||
}
|
||||
|
||||
func (this *ParentAction) SecondMenu(menuItem string) {
|
||||
this.Data["secondMenuItem"] = menuItem
|
||||
}
|
||||
|
||||
func (this *ParentAction) AdminId() int {
|
||||
return this.Context.GetInt("adminId")
|
||||
}
|
||||
|
||||
func (this *ParentAction) CreateLog(level string, description string, args ...interface{}) {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
return
|
||||
}
|
||||
_, err = rpcClient.AdminRPC().CreateLog(rpcClient.Context(this.AdminId()), &admin.CreateLogRequest{
|
||||
Level: level,
|
||||
Description: fmt.Sprintf(description, args...),
|
||||
Action: this.Request.URL.Path,
|
||||
Ip: this.RequestRemoteIP(),
|
||||
})
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
}
|
||||
41
internal/web/actions/actionutils/tabbar.go
Normal file
41
internal/web/actions/actionutils/tabbar.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package actionutils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// Tabbar定义
|
||||
type Tabbar struct {
|
||||
items []maps.Map
|
||||
}
|
||||
|
||||
// 获取新对象
|
||||
func NewTabbar() *Tabbar {
|
||||
return &Tabbar{
|
||||
items: []maps.Map{},
|
||||
}
|
||||
}
|
||||
|
||||
// 添加菜单项
|
||||
func (this *Tabbar) Add(name string, subName string, url string, icon string, active bool) maps.Map {
|
||||
m := maps.Map{
|
||||
"name": name,
|
||||
"subName": subName,
|
||||
"url": url,
|
||||
"icon": icon,
|
||||
"active": active,
|
||||
}
|
||||
this.items = append(this.items, m)
|
||||
return m
|
||||
}
|
||||
|
||||
// 取得所有的Items
|
||||
func (this *Tabbar) Items() []maps.Map {
|
||||
return this.items
|
||||
}
|
||||
|
||||
// 设置子菜单
|
||||
func SetTabbar(action actions.ActionWrapper, tabbar *Tabbar) {
|
||||
action.Object().Data["teaTabbar"] = tabbar.Items()
|
||||
}
|
||||
66
internal/web/actions/actionutils/utils.go
Normal file
66
internal/web/actions/actionutils/utils.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package actionutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 提示服务器错误信息
|
||||
func Fail(action actions.ActionWrapper, err error) {
|
||||
if err != nil {
|
||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||
}
|
||||
action.Object().Fail(teaconst.ErrServer)
|
||||
}
|
||||
|
||||
// 提示页面错误信息
|
||||
func FailPage(action actions.ActionWrapper, err error) {
|
||||
if err != nil {
|
||||
logs.Println("[" + reflect.TypeOf(action).String() + "]" + findStack(err.Error()))
|
||||
}
|
||||
action.Object().WriteString(teaconst.ErrServer)
|
||||
}
|
||||
|
||||
// 判断动作的文件路径是否相当
|
||||
func MatchPath(action *actions.ActionObject, path string) bool {
|
||||
return action.Request.URL.Path == path
|
||||
}
|
||||
|
||||
func findStack(err string) string {
|
||||
_, currentFilename, _, currentOk := runtime.Caller(1)
|
||||
if currentOk {
|
||||
for i := 1; i < 32; i++ {
|
||||
_, filename, lineNo, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
if filename == currentFilename || filepath.Base(filename) == "parent_action.go" {
|
||||
continue
|
||||
}
|
||||
|
||||
goPath := os.Getenv("GOPATH")
|
||||
if len(goPath) > 0 {
|
||||
absGoPath, err := filepath.Abs(goPath)
|
||||
if err == nil {
|
||||
filename = strings.TrimPrefix(filename, absGoPath)[1:]
|
||||
}
|
||||
} else if strings.Contains(filename, "src") {
|
||||
filename = filename[strings.Index(filename, "src"):]
|
||||
}
|
||||
|
||||
err += "\n\t\t" + string(filename) + ":" + fmt.Sprintf("%d", lineNo)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user