save
This commit is contained in:
		
							parent
							
								
									7fb270308d
								
							
						
					
					
						commit
						e9d01aa19f
					
				@ -22,7 +22,7 @@ func initCommonResourcesRepo(db *gorm.DB) {
 | 
			
		||||
	r(consts.ResourcesName_Account, "账号管理", repo.NewCommonResourceRepo(db, &model.Account{}), ShowMethod_Get) // 账号管理不需要在后台读写数据,都是通过项目api拉
 | 
			
		||||
	r(consts.ResourcesName_Role, "角色管理", repo.NewCommonResourceRepo(db, &model.Role{}), ShowMethod_Get)       // 角色管理不需要在后台读写数据,都是通过项目api拉
 | 
			
		||||
	r(consts.ResourcesName_WhiteList, "白名单", repo.NewCommonResourceRepo(db, &model.WhiteList{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Delete)
 | 
			
		||||
	r(consts.ResourcesName_Ban, "封禁管理", repo.NewCommonResourceRepo(db, &model.Ban{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Delete)
 | 
			
		||||
	r(consts.ResourcesName_Ban, "封禁管理", repo.NewCommonResourceRepo(db, &model.Ban{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
 | 
			
		||||
	r(consts.ResourcesName_MailRole, "个人邮件", repo.NewCommonResourceRepo(db, &model.RoleMail{}), ShowMethod_Get|ShowMethod_Post)                       // 个人邮件发放就没法撤回?
 | 
			
		||||
	r(consts.ResourcesName_MailGlobal, "全服邮件", repo.NewCommonResourceRepo(db, &model.GlobalMail{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Delete) // 直接删除,别修改了,玩家收到的更乱
 | 
			
		||||
	r(consts.ResourcesName_Notice, "公告", repo.NewCommonResourceRepo(db, &model.Notice{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import (
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -27,6 +28,10 @@ func poToCommonDtoNo(po any) dto.CommonDtoValues {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		obj[ft.Name] = fo.Interface()
 | 
			
		||||
 | 
			
		||||
		if ft.Type.Name() == "Time" && ft.Type.PkgPath() == "time" {
 | 
			
		||||
			obj[ft.Name] = fo.Interface().(time.Time).Format(time.DateTime)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return obj
 | 
			
		||||
@ -46,6 +51,11 @@ func getFieldTypeDtoDescInfo(projectId string, poValue reflect.Value, fieldType
 | 
			
		||||
		Required:    fieldType.Tag.Get("required") == "true",
 | 
			
		||||
		Choices:     make([]*dto.CommonDtoFieldChoice, 0),
 | 
			
		||||
		MultiChoice: fieldType.Tag.Get("multi_choice") == "true",
 | 
			
		||||
		Uneditable:  fieldType.Tag.Get("uneditable") == "true",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f1.Key == "CreatedAt" {
 | 
			
		||||
		f1.Name = "创建时间"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tagType := fieldType.Tag.Get("type"); tagType != "" {
 | 
			
		||||
@ -114,7 +124,9 @@ func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		typeName := field.Type.Name()
 | 
			
		||||
		if typeName == "Time" {
 | 
			
		||||
			return reflect.ValueOf(time.Now())
 | 
			
		||||
			xlog.Debugf("time content:%v", setValue)
 | 
			
		||||
			t, _ := time.ParseInLocation("2006/01/02 15:04:05", setValue, time.Local)
 | 
			
		||||
			return reflect.ValueOf(t)
 | 
			
		||||
		}
 | 
			
		||||
		if typeName == "DeletedAt" {
 | 
			
		||||
			return reflect.ValueOf(gorm.DeletedAt{})
 | 
			
		||||
@ -124,16 +136,29 @@ func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue
 | 
			
		||||
		typeName := field.Type.String()
 | 
			
		||||
		if typeName == "[]string" {
 | 
			
		||||
			list := make([]string, 0)
 | 
			
		||||
			if _, ok := rawValue.(string); ok {
 | 
			
		||||
			if rawValueStr, ok := rawValue.(string); ok {
 | 
			
		||||
				elems := strings.Split(rawValueStr, ",")
 | 
			
		||||
				list = append(list, elems...)
 | 
			
		||||
				// 空的字符串
 | 
			
		||||
			} else {
 | 
			
		||||
				if rawValueList, ok := rawValue.([]string); ok {
 | 
			
		||||
					for _, v := range rawValueList {
 | 
			
		||||
						list = append(list, v)
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					for _, v := range rawValue.([]interface{}) {
 | 
			
		||||
						list = append(list, v.(string))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			parsedValue = list
 | 
			
		||||
		} else if typeName == "[]*model.MailAttachItem" {
 | 
			
		||||
			items := make([]*model.MailAttachItem, 0)
 | 
			
		||||
			if rawValueList, ok := rawValue.([]*model.MailAttachItem); ok {
 | 
			
		||||
				for _, item := range rawValueList {
 | 
			
		||||
					items = append(items, item)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				for _, itemI := range rawValue.([]interface{}) {
 | 
			
		||||
					item := &model.MailAttachItem{}
 | 
			
		||||
					for k, vI := range itemI.(map[string]any) {
 | 
			
		||||
@ -148,6 +173,7 @@ func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue
 | 
			
		||||
					}
 | 
			
		||||
					items = append(items, item)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			parsedValue = items
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ type ProjectService struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewProjectService(db *gorm.DB) *ProjectService {
 | 
			
		||||
	repo.ServerRepoInstance = repo.NewServerRepo(db)
 | 
			
		||||
	return &ProjectService{repo: repo.NewProjectRepo(db)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,8 @@ var projectsResourceHookMgr = map[string]map[string]any{
 | 
			
		||||
		consts.ResourcesName_Role:       &smdl.RoleHook{},       // 所有角色走神魔大陆api直接获取
 | 
			
		||||
		consts.ResourcesName_Account:    &smdl.AccountHook{},    // 所有角色走神魔大陆api直接获取
 | 
			
		||||
		consts.ResourcesName_Ban:        &smdl.BanHook{},        // 所有角色走神魔大陆api直接获取
 | 
			
		||||
		consts.ResourcesName_MailGlobal: &smdl.MailGlobalHook{}, // 所有角色走神魔大陆api直接获取
 | 
			
		||||
		consts.ResourcesName_MailRole:   &smdl.MailRoleHook{},   // 所有角色走神魔大陆api直接获取
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,8 @@ import (
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/errcode"
 | 
			
		||||
	"admin/lib/httpclient"
 | 
			
		||||
	"admin/lib/utils"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -26,7 +28,7 @@ func (hook *AccountHook) List(projectInfo *entity.Project, resource string, page
 | 
			
		||||
			Data []struct {
 | 
			
		||||
				Account  string  `json:"account"`
 | 
			
		||||
				ServerId int     `json:"serverId"`
 | 
			
		||||
				RoleList []*model.Role `json:"roleList"`
 | 
			
		||||
				RoleList []*Role `json:"roleList"`
 | 
			
		||||
			} `json:"data"`
 | 
			
		||||
		} `json:"data"`
 | 
			
		||||
	}
 | 
			
		||||
@ -43,9 +45,19 @@ func (hook *AccountHook) List(projectInfo *entity.Project, resource string, page
 | 
			
		||||
			Account:      user.Account,
 | 
			
		||||
			ServerConfId: strconv.Itoa(user.ServerId),
 | 
			
		||||
		}
 | 
			
		||||
		latestLoginTime := int64(0)
 | 
			
		||||
		earliestCreateTime := int64(math.MaxInt64)
 | 
			
		||||
		for _, v := range user.RoleList {
 | 
			
		||||
			userPo.RoleIds = append(userPo.RoleIds, v.RoleId)
 | 
			
		||||
			if latestLoginTime < v.LatestLoginTime {
 | 
			
		||||
				latestLoginTime = v.LatestLoginTime
 | 
			
		||||
			}
 | 
			
		||||
			if earliestCreateTime > v.CreateTime {
 | 
			
		||||
				earliestCreateTime = v.CreateTime
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		userPo.LatestLoginTime = utils.ParseUnixTimestamp2LocalTime(latestLoginTime)
 | 
			
		||||
		userPo.CreateTime = utils.ParseUnixTimestamp2LocalTime(earliestCreateTime)
 | 
			
		||||
		et := (&entity.CommonResource{}).FromPo(userPo)
 | 
			
		||||
		rows = append(rows, et.ToCommonDto())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -28,13 +28,15 @@ func (hook *BanHook) Create(projectInfo *entity.Project, resource string, dtoObj
 | 
			
		||||
	params.Add("server", banInfo.ServerConfID)
 | 
			
		||||
	params.Add("roleid", roleId)
 | 
			
		||||
 | 
			
		||||
	if banInfo.ExpireAt <= 0 {
 | 
			
		||||
	expireAt := banInfo.ExpireAt.Unix()
 | 
			
		||||
 | 
			
		||||
	if expireAt <= 0 {
 | 
			
		||||
		// 解封
 | 
			
		||||
		params.Add("forbidtime", "0")
 | 
			
		||||
		params.Add("desc", "待写原因")
 | 
			
		||||
		params.Add("desc", "你被永久封禁了")
 | 
			
		||||
	} else {
 | 
			
		||||
		dura := banInfo.ExpireAt - time.Now().Unix()
 | 
			
		||||
		dura := expireAt - time.Now().Unix()
 | 
			
		||||
		if dura <= 0 {
 | 
			
		||||
			// 解封
 | 
			
		||||
			params.Add("forbidtime", "0")
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,12 @@ package smdl
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/apps/game/domain/entity"
 | 
			
		||||
	"admin/apps/game/domain/repo"
 | 
			
		||||
	"admin/apps/game/model"
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/errcode"
 | 
			
		||||
	"admin/lib/httpclient"
 | 
			
		||||
	"admin/lib/xlog"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
@ -45,7 +47,15 @@ func (hook *MailGlobalHook) Create(projectInfo *entity.Project, resource string,
 | 
			
		||||
	serverIdsLen := len(mailInfo.ServerIDs)
 | 
			
		||||
	switch {
 | 
			
		||||
	case serverIdsLen == 0:
 | 
			
		||||
		// todo 所有服
 | 
			
		||||
		// 为空就发给所有服
 | 
			
		||||
		serverList, err := repo.ServerRepoInstance.List(projectInfo.ProjectPo.ProjectId)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			xlog.Errorf("list all server by project:%v error:%v", projectInfo.ProjectPo.ProjectId, err)
 | 
			
		||||
			return errcode.New(errcode.ServerError, "list all server by project:%v error:%v", projectInfo.ProjectPo.ProjectId, err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, v := range serverList {
 | 
			
		||||
			serverIds = append(serverIds, v.ToPo().(*model.Server).ServerConfID)
 | 
			
		||||
		}
 | 
			
		||||
	case serverIdsLen > 0:
 | 
			
		||||
		for _, v := range mailInfo.ServerIDs {
 | 
			
		||||
			serverIds = append(serverIds, v)
 | 
			
		||||
@ -60,8 +70,16 @@ func (hook *MailGlobalHook) Create(projectInfo *entity.Project, resource string,
 | 
			
		||||
 | 
			
		||||
	// 通知对应服务器
 | 
			
		||||
	for _, serverId := range serverIds {
 | 
			
		||||
		serverIdInt, _ := strconv.Atoi(serverId)
 | 
			
		||||
		if serverIdInt == 0 || serverIdInt%10000 > 500 {
 | 
			
		||||
			// 过滤掉20801 28802这些跨服
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		params.Set("server", serverId)
 | 
			
		||||
 | 
			
		||||
		xlog.Debugf("通知神魔大陆区服%v添加邮件:%v", serverId, params.Encode())
 | 
			
		||||
 | 
			
		||||
		rsp := make(map[string]any)
 | 
			
		||||
		err := httpclient.Request(alisrvAddr+"/mail_global", "get", params, &rsp)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ import (
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/errcode"
 | 
			
		||||
	"admin/lib/httpclient"
 | 
			
		||||
	"admin/lib/xlog"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
@ -49,6 +50,8 @@ func (hook *MailRoleHook) Create(projectInfo *entity.Project, resource string, d
 | 
			
		||||
 | 
			
		||||
	// 通知对应服务器
 | 
			
		||||
	for _, roleId := range mailInfo.RoleIDs {
 | 
			
		||||
		params.Set("playerid", roleId)
 | 
			
		||||
		xlog.Debugf("发送神魔大陆个人邮件给角色:%v, 内容:%v", roleId, params.Encode())
 | 
			
		||||
		rsp := make(map[string]any)
 | 
			
		||||
		err := httpclient.Request(alisrvAddr+"/mail_role", "get", params, &rsp)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -6,8 +6,21 @@ import (
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/errcode"
 | 
			
		||||
	"admin/lib/httpclient"
 | 
			
		||||
	"admin/lib/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Role struct {
 | 
			
		||||
	RoleId          string `json:"roleId"`
 | 
			
		||||
	Account         string `json:"account"`
 | 
			
		||||
	ServerConfId    string `json:"serverId"`
 | 
			
		||||
	Name            string `json:"roleName"`
 | 
			
		||||
	Status          string `json:"status"`
 | 
			
		||||
	Level           int    `json:"roleLevel"`
 | 
			
		||||
	Profession      string `json:"profession"`
 | 
			
		||||
	LatestLoginTime int64  `json:"latestLoginAt"`
 | 
			
		||||
	CreateTime      int64  `json:"createAt"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RoleHook struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,7 +35,7 @@ func (hook *RoleHook) List(projectInfo *entity.Project, resource string, pageNo,
 | 
			
		||||
		Code int    `json:"code"`
 | 
			
		||||
		Msg  string `json:"msg"`
 | 
			
		||||
		Data struct {
 | 
			
		||||
			Data []*model.Role `json:"data"`
 | 
			
		||||
			Data []*Role `json:"data"`
 | 
			
		||||
		} `json:"data"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -33,7 +46,18 @@ func (hook *RoleHook) List(projectInfo *entity.Project, resource string, pageNo,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, role := range rsp.Data.Data {
 | 
			
		||||
		et := (&entity.CommonResource{}).FromPo(role)
 | 
			
		||||
		rolePo := &model.Role{
 | 
			
		||||
			RoleId:          role.RoleId,
 | 
			
		||||
			Account:         role.Account,
 | 
			
		||||
			ServerConfId:    role.ServerConfId,
 | 
			
		||||
			Name:            role.Name,
 | 
			
		||||
			Status:          role.Status,
 | 
			
		||||
			Level:           role.Level,
 | 
			
		||||
			Profession:      role.Profession,
 | 
			
		||||
			LatestLoginTime: utils.ParseUnixTimestamp2LocalTime(role.LatestLoginTime),
 | 
			
		||||
			CreateTime:      utils.ParseUnixTimestamp2LocalTime(role.CreateTime),
 | 
			
		||||
		}
 | 
			
		||||
		et := (&entity.CommonResource{}).FromPo(rolePo)
 | 
			
		||||
		rows = append(rows, et.ToCommonDto())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,8 +25,9 @@ func (hook *ServerHook) List(projectInfo *entity.Project, resource string, pageN
 | 
			
		||||
		HelpText:    "进程运行状态:未知、运行中、停止",
 | 
			
		||||
		Readonly:    false,
 | 
			
		||||
		Required:    false,
 | 
			
		||||
		Choices:     nil,
 | 
			
		||||
		Choices:     make([]*dto.CommonDtoFieldChoice, 0),
 | 
			
		||||
		MultiChoice: false,
 | 
			
		||||
		Uneditable:  true,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	type ServerInfo struct {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								admin/apps/game/domain/repo/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								admin/apps/game/domain/repo/server.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
package repo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/apps/game/domain/entity"
 | 
			
		||||
	"admin/apps/game/model"
 | 
			
		||||
	"admin/internal/errcode"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ServerRepoInstance IServerRepo
 | 
			
		||||
 | 
			
		||||
type IServerRepo interface {
 | 
			
		||||
	List(projectId string) ([]*entity.CommonResource, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewServerRepo(db *gorm.DB) IServerRepo {
 | 
			
		||||
	return &serverRepoImpl{db: db}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type serverRepoImpl struct {
 | 
			
		||||
	db *gorm.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (impl *serverRepoImpl) List(projectId string) ([]*entity.CommonResource, error) {
 | 
			
		||||
	list := make([]*model.Server, 0)
 | 
			
		||||
	err := impl.db.Where("project_id = ?", projectId).Find(&list).Error
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errcode.New(errcode.DBError, "list server error:%v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	list1 := make([]*entity.CommonResource, 0, len(list))
 | 
			
		||||
	for _, v := range list {
 | 
			
		||||
		list1 = append(list1, (&entity.CommonResource{}).FromPo(v))
 | 
			
		||||
	}
 | 
			
		||||
	return list1, nil
 | 
			
		||||
}
 | 
			
		||||
@ -2,6 +2,7 @@ package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
@ -13,8 +14,8 @@ type Account struct {
 | 
			
		||||
	Account         string    `name:"账号" json:"account"`
 | 
			
		||||
	ServerConfId    string    `name:"区服id" json:"serveId"`
 | 
			
		||||
	RoleIds         []string  `gorm:"type:json;serializer:json" name:"角色id列表" json:"roleIds"`
 | 
			
		||||
	LatestLoginTime int64    `name:"最近登录时间" json:"latest_login_time"`
 | 
			
		||||
	CreateTime      int64    `name:"创建时间" json:"create_time"`
 | 
			
		||||
	LatestLoginTime time.Time `name:"最近登录时间" json:"latest_login_time"`
 | 
			
		||||
	CreateTime      time.Time `name:"创建时间" json:"create_time"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *Account) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -14,14 +13,13 @@ func init() {
 | 
			
		||||
type Ban struct {
 | 
			
		||||
	ID           int       `gorm:"primarykey" readonly:"true"`
 | 
			
		||||
	ProjectId    string    `gorm:"type:varchar(255);uniqueIndex:idx_ban"`
 | 
			
		||||
	ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" required:"true"`
 | 
			
		||||
	BanType      string `name:"封禁类型" required:"true" choices:"GetBanTypeChoices" multi_choice:"true"`
 | 
			
		||||
	Value        string `gorm:"type:varchar(128);uniqueIndex:idx_ban" name:"封禁账号" required:"true" desc:"填账号"`
 | 
			
		||||
	ExpireAt     int64  `name:"封禁到期时间" desc:"封禁到期时间,0表示永久封禁"`
 | 
			
		||||
	ServerConfID string    `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetServerConfIDChoices" uneditable:"true"`
 | 
			
		||||
	BanType      string    `name:"封禁类型" required:"true" choices:"GetBanTypeChoices" uneditable:"true"`
 | 
			
		||||
	Value        string    `gorm:"type:varchar(128);uniqueIndex:idx_ban" name:"封禁值" required:"true" desc:"根据封禁类型填对应值,例如ip就填ip地址" uneditable:"true"`
 | 
			
		||||
	BanReason    string    `name:"封禁理由" desc:"封禁理由,会推送给客户端弹窗" required:"true"`
 | 
			
		||||
	ExpireAt     time.Time `name:"封禁到期时间" desc:"封禁到期时间,0表示永久封禁"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *Ban) TableName() string {
 | 
			
		||||
@ -32,15 +30,35 @@ func (m *Ban) GetId() int {
 | 
			
		||||
	return m.ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Ban) GetServerConfIDChoices(projectId string) []*dto.CommonDtoFieldChoice {
 | 
			
		||||
	return getChoiceServers(projectId)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Ban) GetBanTypeChoices(projectId string) []*dto.CommonDtoFieldChoice {
 | 
			
		||||
	return []*dto.CommonDtoFieldChoice{
 | 
			
		||||
		{
 | 
			
		||||
			Desc:  "作弊",
 | 
			
		||||
			Value: "作弊",
 | 
			
		||||
			Desc:  "账号",
 | 
			
		||||
			Value: "account",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Desc:  "广告",
 | 
			
		||||
			Value: "广告",
 | 
			
		||||
			Desc:  "角色",
 | 
			
		||||
			Value: "role",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Desc:  "IP",
 | 
			
		||||
			Value: "ip",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Desc:  "账号发言",
 | 
			
		||||
			Value: "account_chat",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Desc:  "角色发言",
 | 
			
		||||
			Value: "role_chat",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Desc:  "设备号",
 | 
			
		||||
			Value: "device_id",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@ package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -18,8 +17,6 @@ type DevicePush struct {
 | 
			
		||||
	Content   string `name:"通知内容"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *DevicePush) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@ type CommonDtoFieldDesc struct {
 | 
			
		||||
	Required    bool                    `json:"required"`     // 是否必填,不能为空
 | 
			
		||||
	Choices     []*CommonDtoFieldChoice `json:"choices"`      // 可选项,用于字段做下拉框
 | 
			
		||||
	MultiChoice bool                    `json:"multi_choice"` // 是否多选
 | 
			
		||||
	Uneditable  bool                    `json:"uneditable"`   // 不可编辑,某些数据一旦新增之后不能修改,例如封禁的值、服务器的id等
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//type CommonDtoValue struct {
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -22,8 +21,6 @@ type GlobalMail struct {
 | 
			
		||||
	Attach    []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" choices:"GetChoiceItems"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *GlobalMail) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,8 @@ type IModel interface {
 | 
			
		||||
 | 
			
		||||
var GetProjectServersHandler func(projectId string) ([]*Server, error)
 | 
			
		||||
 | 
			
		||||
func getChoiceServers(args ...any) []*dto.CommonDtoFieldChoice {
 | 
			
		||||
	servers, err := GetProjectServersHandler(args[0].(string))
 | 
			
		||||
func getChoiceServers(projectId string) []*dto.CommonDtoFieldChoice {
 | 
			
		||||
	servers, err := GetProjectServersHandler(projectId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -16,12 +15,10 @@ type Notice struct {
 | 
			
		||||
	ProjectId string
 | 
			
		||||
	ServerIDs []int     `gorm:"type:json;serializer:json" name:"公告生效服务器" desc:"为空表示所有服" choices:"GetChoiceServers"`
 | 
			
		||||
	Content   string    `name:"公告内容" required:"true"`
 | 
			
		||||
	StartAt   int64  `name:"开始时间" required:"true"`
 | 
			
		||||
	EndAt     int64  `name:"结束时间" required:"true"`
 | 
			
		||||
	StartAt   time.Time `name:"开始时间" required:"true"`
 | 
			
		||||
	EndAt     time.Time `name:"结束时间" required:"true"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *Notice) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@ package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -14,7 +13,7 @@ func init() {
 | 
			
		||||
type Project struct {
 | 
			
		||||
	ID        int    `gorm:"primarykey" readonly:"true"`
 | 
			
		||||
	ProjectId string `gorm:"type:varchar(255);unique" name:"项目id" readonly:"true"`
 | 
			
		||||
	Name      string `gorm:"primarykey" required:"true" name:"项目名" readonly:"true"`
 | 
			
		||||
	Name      string `gorm:"primarykey" required:"true" name:"项目名" readonly:"true" uneditable:"true"`
 | 
			
		||||
	Desc      string `name:"项目描述"`
 | 
			
		||||
	// command_list接口服务器地址,为空代表由由项目下各个逻辑服提供command_list.
 | 
			
		||||
	// 取决于每个项目改造难度:
 | 
			
		||||
@ -24,8 +23,6 @@ type Project struct {
 | 
			
		||||
	ApiAddr string `name:"游戏api地址" desc:"api服务器地址,例如神魔大陆就是alisrv服务器地址,用于后台调用gm"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *Project) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@ package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -17,8 +16,6 @@ type RewardCode struct {
 | 
			
		||||
	Code      int `name:"奖励码"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *RewardCode) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
@ -10,15 +11,15 @@ func init() {
 | 
			
		||||
 | 
			
		||||
// Role 空表,就是用来兼容资源增删改查公共操作的,实际列举账号等都是走各个项目api拉取
 | 
			
		||||
type Role struct {
 | 
			
		||||
	RoleId          string `name:"账号" json:"roleId"`
 | 
			
		||||
	RoleId          string    `name:"角色ID" json:"roleId"`
 | 
			
		||||
	Account         string    `name:"账号" json:"account"`
 | 
			
		||||
	ServerConfId    string    `name:"区服id" json:"serverId"`
 | 
			
		||||
	Name            string    `name:"名称" json:"roleName"`
 | 
			
		||||
	Status          string    `name:"状态" desc:"离线|在线" json:"status"`
 | 
			
		||||
	Level           int       `name:"等级" json:"roleLevel"`
 | 
			
		||||
	Profession      string    `name:"职业" json:"profession"`
 | 
			
		||||
	LatestLoginTime int64  `name:"最近登录时间" json:"latestLoginAt"`
 | 
			
		||||
	CreateTime      int64  `name:"创建时间" json:"createAt"`
 | 
			
		||||
	LatestLoginTime time.Time `name:"最近登录时间" json:"latestLoginAt"`
 | 
			
		||||
	CreateTime      time.Time `name:"创建时间" json:"createAt"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *Role) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ package model
 | 
			
		||||
import (
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -20,15 +19,13 @@ type MailAttachItem struct {
 | 
			
		||||
type RoleMail struct {
 | 
			
		||||
	ID        int `gorm:"primarykey" readonly:"true"`
 | 
			
		||||
	ProjectId string
 | 
			
		||||
	RoleIDs   []string          `gorm:"type:json;serializer:json" name:"生效的角色id" required:"true"`
 | 
			
		||||
	ServerID  string            `name:"所属区服" choices:"GetChoiceServers" multi_choice:"false"`
 | 
			
		||||
	RoleIDs   []string          `gorm:"type:json;serializer:json" name:"生效的角色id" desc:"生效的角色id,逗号分隔多个" required:"true"`
 | 
			
		||||
	ServerID  string            `name:"所属区服" choices:"GetChoiceServers" required:"true"`
 | 
			
		||||
	Title     string            `name:"邮件标题" required:"true"`
 | 
			
		||||
	Content   string            `name:"邮件内容" required:"true"`
 | 
			
		||||
	Attach    []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" choices:"GetChoiceItems"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *RoleMail) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@ package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -14,18 +13,16 @@ func init() {
 | 
			
		||||
type Server struct {
 | 
			
		||||
	ID           int    `gorm:"primarykey" readonly:"true"`
 | 
			
		||||
	ProjectId    string `gorm:"type:varchar(200);uniqueIndex:idx_server"`
 | 
			
		||||
	ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" required:"true"`
 | 
			
		||||
	Desc         string
 | 
			
		||||
	ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id"  required:"true" uneditable:"true"`
 | 
			
		||||
	Desc         string `name:"描述"`
 | 
			
		||||
	// command_list接口服务器地址,为空代表由由项目统一提供command_list.
 | 
			
		||||
	// 取决于每个项目改造难度:
 | 
			
		||||
	//   为空就代表项目要实现一个自己统一对外暴露的gm调用服务对内聚合、分发指令执行,本后台执行指令只调用一次;
 | 
			
		||||
	//   不为空就代表command_list实现在各个逻辑服,由本后台系统在执行指令时聚合、分发指令
 | 
			
		||||
	//   调用各个逻辑服执行,本后台执行指令需要根据逻辑服数量调用;
 | 
			
		||||
	ApiAddr string
 | 
			
		||||
	//ApiAddr string
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *Server) TableName() string {
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"admin/apps/game/model/dto"
 | 
			
		||||
	"admin/internal/db"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -13,14 +13,11 @@ func init() {
 | 
			
		||||
type WhiteList struct {
 | 
			
		||||
	ID           int    `gorm:"primarykey" readonly:"true"`
 | 
			
		||||
	ProjectId    string `gorm:"type:varchar(256);uniqueIndex:idx_whitelist"`
 | 
			
		||||
	ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" required:"true"`
 | 
			
		||||
	Account      string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist"`
 | 
			
		||||
	AccountType  int    `gorm:"uniqueIndex:idx_whitelist"`
 | 
			
		||||
	Desc         string
 | 
			
		||||
	ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers"`
 | 
			
		||||
	Account      string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist" name:"账户" required:"true"`
 | 
			
		||||
	Desc         string `name:"描述"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `readonly:"true"`
 | 
			
		||||
	UpdatedAt time.Time      `readonly:"true"`
 | 
			
		||||
	DeletedAt gorm.DeletedAt `gorm:"index" readonly:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lm *WhiteList) TableName() string {
 | 
			
		||||
@ -30,3 +27,7 @@ func (lm *WhiteList) TableName() string {
 | 
			
		||||
func (m *WhiteList) GetId() int {
 | 
			
		||||
	return m.ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WhiteList) GetChoiceServers(projectId string) []*dto.CommonDtoFieldChoice {
 | 
			
		||||
	return getChoiceServers(projectId)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								admin/lib/utils/time.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								admin/lib/utils/time.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ParseUnixTimestamp2LocalTime(ts int64) (local time.Time) {
 | 
			
		||||
	sec := int64(0)
 | 
			
		||||
	nsec := int64(0)
 | 
			
		||||
	switch {
 | 
			
		||||
	case ts >= 1e18: // 纳秒
 | 
			
		||||
		sec = ts / int64(1e9)
 | 
			
		||||
		nsec = ts % int64(1e9)
 | 
			
		||||
	case ts >= 1e15: // 微妙
 | 
			
		||||
		sec = ts / int64(1e6)
 | 
			
		||||
		nsec = ts % int64(1e6)
 | 
			
		||||
	case ts >= 1e12: // 毫秒
 | 
			
		||||
		sec = ts / int64(1e3)
 | 
			
		||||
		nsec = ts % int64(1e3)
 | 
			
		||||
	case ts >= 1e9: // 秒
 | 
			
		||||
		sec = ts
 | 
			
		||||
		nsec = 0
 | 
			
		||||
	default:
 | 
			
		||||
		panic(fmt.Errorf("ts:%v invalid parse to local time"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t := time.Unix(sec, nsec)
 | 
			
		||||
	ts2str := t.Format("2006-01-02 15:04:05.000000000")
 | 
			
		||||
	local, _ = time.ParseInLocation("2006-01-02 15:04:05.000000000", ts2str, time.Local)
 | 
			
		||||
	return local
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTimeZoneLocalTime 获取指定时区的当地时间
 | 
			
		||||
func GetTimeZoneLocalTime(timeZone int) time.Time {
 | 
			
		||||
	offsetZone := time.FixedZone(time.Local.String(), timeZone*60*60)
 | 
			
		||||
	return time.Now().In(offsetZone)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetTimeZoneLocalTimeByTimestamp 获取指定时间戳与时区的当地时间
 | 
			
		||||
func GetTimeZoneLocalTimeByTimestamp(ts int64, timeZone int) time.Time {
 | 
			
		||||
	offsetZone := time.FixedZone(time.Local.String(), timeZone*60*60)
 | 
			
		||||
	return time.Unix(ts, 0).Local().In(offsetZone)
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,29 @@
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
  min-height: 100vh;
 | 
			
		||||
  color: var(--color-text);
 | 
			
		||||
  background: var(--color-background);
 | 
			
		||||
  transition: color 0.5s,
 | 
			
		||||
  background-color 0.5s;
 | 
			
		||||
  line-height: 1.6;
 | 
			
		||||
  font-family: Inter,
 | 
			
		||||
  -apple-system,
 | 
			
		||||
  BlinkMacSystemFont,
 | 
			
		||||
  'Segoe UI',
 | 
			
		||||
  Roboto,
 | 
			
		||||
  Oxygen,
 | 
			
		||||
  Ubuntu,
 | 
			
		||||
  Cantarell,
 | 
			
		||||
  'Fira Sans',
 | 
			
		||||
  'Droid Sans',
 | 
			
		||||
  'Helvetica Neue',
 | 
			
		||||
  sans-serif;
 | 
			
		||||
  font-size: 15px;
 | 
			
		||||
  text-rendering: optimizeLegibility;
 | 
			
		||||
  -webkit-font-smoothing: antialiased;
 | 
			
		||||
  -moz-osx-font-smoothing: grayscale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-table {
 | 
			
		||||
  .el-table__header-wrapper, .el-table__fixed-header-wrapper {
 | 
			
		||||
    th {
 | 
			
		||||
 | 
			
		||||
@ -39,11 +39,13 @@ const listData = async () => {
 | 
			
		||||
    for (let i = 0; i < fieldsDescInfo.value.length; i++) {
 | 
			
		||||
      var field = fieldsDescInfo.value[i]
 | 
			
		||||
      dialogAddForm.value[field.key] = ''
 | 
			
		||||
 | 
			
		||||
      if (field.required == true) {
 | 
			
		||||
        rules.value[field.key] = [{required: true, message: field.name + "不能为空", trigger: "blur"}]
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (field.type == "items") {
 | 
			
		||||
        dialogAddForm.value[field.key] = []
 | 
			
		||||
        for (let j = 0; j < rows.value.length; j++) {
 | 
			
		||||
          rows.value[j].jsonValue = JSON.stringify(rows.value[j][field.key])
 | 
			
		||||
        }
 | 
			
		||||
@ -71,6 +73,7 @@ const dialogEditFormRef = ref(null)
 | 
			
		||||
 | 
			
		||||
const dialogAddForm = ref({
 | 
			
		||||
  ServerIDs: [],
 | 
			
		||||
  Attach: [],
 | 
			
		||||
})
 | 
			
		||||
const dialogEditForm = ref({})
 | 
			
		||||
 | 
			
		||||
@ -90,6 +93,7 @@ const submitAdd = async () => {
 | 
			
		||||
          })
 | 
			
		||||
          rows.value.push(res.data.dto)
 | 
			
		||||
          dialogAddVisible.value = false
 | 
			
		||||
          handleCloseDialog()
 | 
			
		||||
        }, (err) => {
 | 
			
		||||
          console.log("添加报错:", err)
 | 
			
		||||
        })
 | 
			
		||||
@ -115,6 +119,7 @@ const submitEdit = async () => {
 | 
			
		||||
          })
 | 
			
		||||
          dialogEditVisible.value = false
 | 
			
		||||
          rows.value[dialogEditForm.value.oldIndex] = res.data.dto
 | 
			
		||||
          handleCloseDialog()
 | 
			
		||||
        }, (err) => {
 | 
			
		||||
          console.log("添加报错:", err)
 | 
			
		||||
        })
 | 
			
		||||
@ -135,10 +140,11 @@ const handleEdit = (index, row) => {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const handleDelete = (index, row) => {
 | 
			
		||||
  ElMessageBox.confirm("确定要删除吗?").then(() => {
 | 
			
		||||
    resourceDelete(resource_url, {id: row.ID}).then((res) => {
 | 
			
		||||
      ElNotification({
 | 
			
		||||
        title: "删除结果通知",
 | 
			
		||||
      message: "删除指令服务器[" + row.name + "]成功!",
 | 
			
		||||
        message: "删除数据[" + row.ID + "]成功!",
 | 
			
		||||
        type: 'success',
 | 
			
		||||
        duration: 4000,
 | 
			
		||||
        "show-close": true,
 | 
			
		||||
@ -147,6 +153,9 @@ const handleDelete = (index, row) => {
 | 
			
		||||
    }, (err) => {
 | 
			
		||||
      console.log("delet error:", err)
 | 
			
		||||
    })
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function addItem(fieldDescInfo) {
 | 
			
		||||
@ -184,6 +193,7 @@ function deleteItem(row) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const handleCloseDialog = () => {
 | 
			
		||||
  console.log("关闭添加/编辑弹窗")
 | 
			
		||||
  dialogAddVisible.value = false
 | 
			
		||||
  dialogEditVisible.value = false
 | 
			
		||||
  dialogAddForm.value = {
 | 
			
		||||
@ -213,24 +223,6 @@ const handleCloseDialog = () => {
 | 
			
		||||
        <el-table-column prop="func" label="功 能">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
 | 
			
		||||
            <!--            <el-button size="default" type="primary"-->
 | 
			
		||||
            <!--                       @click="tableRowClick(row_click_btn1, scope.$index, scope.row)"-->
 | 
			
		||||
            <!--                       v-if="(row_click_btn1.text !== '')">-->
 | 
			
		||||
            <!--              <el-icon style="vertical-align: middle">-->
 | 
			
		||||
            <!--                <component :is="row_click_btn1.icon"></component>-->
 | 
			
		||||
            <!--              </el-icon>-->
 | 
			
		||||
            <!--              <span>{{ row_click_btn1.text }}</span>-->
 | 
			
		||||
            <!--            </el-button>-->
 | 
			
		||||
 | 
			
		||||
            <!--            <el-button size="default" type="primary"-->
 | 
			
		||||
            <!--                       @click="tableRowClick(row_click_btn2, scope.$index, scope.row)"-->
 | 
			
		||||
            <!--                       v-if="(row_click_btn2.text !== '')">-->
 | 
			
		||||
            <!--              <el-icon style="vertical-align: middle">-->
 | 
			
		||||
            <!--                <component :is="row_click_btn2.icon"></component>-->
 | 
			
		||||
            <!--              </el-icon>-->
 | 
			
		||||
            <!--              <span>{{ row_click_btn2.text }}</span>-->
 | 
			
		||||
            <!--            </el-button>-->
 | 
			
		||||
 | 
			
		||||
            <el-button size="default" type="success" @click="handleEdit( scope.$index, scope.row)"
 | 
			
		||||
                       v-if="(resource_raw_node.meta.methods.put === true)">
 | 
			
		||||
              <el-icon style="vertical-align: middle">
 | 
			
		||||
@ -251,15 +243,97 @@ const handleCloseDialog = () => {
 | 
			
		||||
 | 
			
		||||
      <el-dialog v-model="dialogAddVisible" :mask="true" title="添加" :modal="true" :before-close="handleCloseDialog"
 | 
			
		||||
                 destroy-on-close>
 | 
			
		||||
        <el-form ref="dialogAddFormRef" :model="dialogAddForm" :rules="rules">
 | 
			
		||||
        <el-form ref="dialogAddFormRef" :model="dialogAddForm" :rules="rules" label-position="right"
 | 
			
		||||
                 label-width="130px">
 | 
			
		||||
          <template v-for="fieldDescInfo in fieldsDescInfo">
 | 
			
		||||
            <!--如何是items类型,就是物品下拉框+道具组合-->
 | 
			
		||||
            <!--如何是[]string类型,就是下拉框或多选框-->
 | 
			
		||||
            <template v-if="(fieldDescInfo.type === 'items')">
 | 
			
		||||
              <el-form :inline="true" :model="item">
 | 
			
		||||
              <el-form :inline="true" :model="item" label-position="right">
 | 
			
		||||
                <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key" label-width="130px">
 | 
			
		||||
                  <el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
 | 
			
		||||
                    <el-select placeholder="--选择道具后填数量点击添加--" v-model="item.id" style="width: 150px"
 | 
			
		||||
                               filterable>
 | 
			
		||||
                      <el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
 | 
			
		||||
                                 :value="info.value"></el-option>
 | 
			
		||||
                    </el-select>
 | 
			
		||||
                  </el-tooltip>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item label="数量" prop="num">
 | 
			
		||||
                  <el-input type="number" v-model="item.num" placeholder="请输入数量" style="width: 150px"/>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
                <el-form-item>
 | 
			
		||||
                  <el-button type="primary" @click="addItem(fieldDescInfo)">添加</el-button>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
              </el-form>
 | 
			
		||||
              <el-form-item label="奖励列表" prop="Attach">
 | 
			
		||||
                <el-table :data="dialogAddForm.Attach" border>
 | 
			
		||||
                  <el-table-column label="道具id" prop="id"/>
 | 
			
		||||
                  <el-table-column label="数量" prop="num"/>
 | 
			
		||||
                  <el-table-column label="操作">
 | 
			
		||||
                    <template #default="scope">
 | 
			
		||||
                      <el-button type="danger" size="small" @click="deleteItem(scope.row)">删除</el-button>
 | 
			
		||||
                    </template>
 | 
			
		||||
                  </el-table-column>
 | 
			
		||||
                </el-table>
 | 
			
		||||
              </el-form-item>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template v-else-if="(fieldDescInfo.readonly !== true)">
 | 
			
		||||
              <!-- 有可选项的字段,走下拉框或者多选框 -->
 | 
			
		||||
              <template v-if="(fieldDescInfo.choices !== undefined && fieldDescInfo.choices.length > 0)">
 | 
			
		||||
                <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
                  <el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
 | 
			
		||||
                    <el-select placeholder="可选项" v-model="item.id" style="width: 150px">
 | 
			
		||||
                    <el-select :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
 | 
			
		||||
                               v-model="dialogAddForm[fieldDescInfo.key]" style="width: 150px"
 | 
			
		||||
                               :multiple="(fieldDescInfo.multi_choice === true)">
 | 
			
		||||
                      <el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
 | 
			
		||||
                                 :value="info.value"></el-option>
 | 
			
		||||
                    </el-select>
 | 
			
		||||
                  </el-tooltip>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
              </template>
 | 
			
		||||
 | 
			
		||||
              <!-- 时间戳字段,展示时间选择器 -->
 | 
			
		||||
              <template v-else-if="(fieldDescInfo.type === 'Time')">
 | 
			
		||||
                <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
                  <el-date-picker v-model="dialogAddForm[fieldDescInfo.key]" type="datetime"
 | 
			
		||||
                                  placeholder="选个时间" format="YYYY/MM/DD HH:mm:ss"
 | 
			
		||||
                                  value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
              </template>
 | 
			
		||||
 | 
			
		||||
              <!-- 否则就是普通字段 -->
 | 
			
		||||
              <template v-else>
 | 
			
		||||
                <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
                  <el-input v-model="dialogAddForm[fieldDescInfo.key]"
 | 
			
		||||
                            :placeholder="fieldDescInfo.help_text"></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
              </template>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
          </template>
 | 
			
		||||
 | 
			
		||||
          <el-form-item>
 | 
			
		||||
            <el-button @click="submitAdd(dialogAddFormRef)" size="large" type="primary">提交</el-button>
 | 
			
		||||
          </el-form-item>
 | 
			
		||||
 | 
			
		||||
        </el-form>
 | 
			
		||||
      </el-dialog>
 | 
			
		||||
 | 
			
		||||
      <el-dialog v-model="dialogEditVisible" :mask="true" title="编辑" :modal="true" :before-close="handleCloseDialog"
 | 
			
		||||
                 destroy-on-close>
 | 
			
		||||
        <el-form ref="dialogEditFormRef" :model="dialogEditForm" :rules="rules" class="operation_form"
 | 
			
		||||
                 label-width="130px">
 | 
			
		||||
          <template v-for="fieldDescInfo in fieldsDescInfo">
 | 
			
		||||
 | 
			
		||||
            <!--如果是items类型,就是物品下拉框+道具组合-->
 | 
			
		||||
            <template v-if="(fieldDescInfo.type === 'items')">
 | 
			
		||||
              <el-form :inline="true" :model="item" label-position="right"
 | 
			
		||||
                       label-width="130px">
 | 
			
		||||
                <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
                  <el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
 | 
			
		||||
                    <el-select placeholder="--选择道具后填数量点击添加--" v-model="item.id" style="width: 150px"
 | 
			
		||||
                               filterable>
 | 
			
		||||
                      <el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
 | 
			
		||||
                                 :value="info.value"></el-option>
 | 
			
		||||
                    </el-select>
 | 
			
		||||
@ -273,7 +347,7 @@ const handleCloseDialog = () => {
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
              </el-form>
 | 
			
		||||
              <el-form-item label="奖励列表" prop="attachmentsList">
 | 
			
		||||
                <el-table :data="dialogAddForm.Attach" border>
 | 
			
		||||
                <el-table :data="dialogEditForm.Attach" border>
 | 
			
		||||
                  <el-table-column label="道具id" prop="id"/>
 | 
			
		||||
                  <el-table-column label="数量" prop="num"/>
 | 
			
		||||
                  <el-table-column label="操作">
 | 
			
		||||
@ -285,10 +359,14 @@ const handleCloseDialog = () => {
 | 
			
		||||
              </el-form-item>
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <template v-else-if="(fieldDescInfo.type === '[]string')">
 | 
			
		||||
            <template v-else-if="(fieldDescInfo.readonly !== true)">
 | 
			
		||||
              <template v-if="(fieldDescInfo.uneditable !== true)">
 | 
			
		||||
                <!-- 有可选项的字段,走下拉框或者多选框 -->
 | 
			
		||||
                <template v-if="(fieldDescInfo.choices !== undefined && fieldDescInfo.choices.length > 0)">
 | 
			
		||||
                  <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
                    <el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start">
 | 
			
		||||
                  <el-select placeholder="可选项" v-model="dialogAddForm.ServerIDs" style="width: 150px"
 | 
			
		||||
                      <el-select :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
 | 
			
		||||
                                 v-model="dialogEditForm[fieldDescInfo.key]" style="width: 150px"
 | 
			
		||||
                                 :multiple="(fieldDescInfo.multi_choice === true)">
 | 
			
		||||
                        <el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"
 | 
			
		||||
                                   :value="info.value"></el-option>
 | 
			
		||||
@ -297,38 +375,38 @@ const handleCloseDialog = () => {
 | 
			
		||||
                  </el-form-item>
 | 
			
		||||
                </template>
 | 
			
		||||
 | 
			
		||||
            <template v-else-if="(fieldDescInfo.readonly !== true)">
 | 
			
		||||
                <!-- 时间戳字段,展示时间选择器 -->
 | 
			
		||||
                <template v-else-if="(fieldDescInfo.type === 'Time')">
 | 
			
		||||
                  <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
                <el-input v-model="dialogAddForm[fieldDescInfo.key]" :placeholder="fieldDescInfo.name"></el-input>
 | 
			
		||||
                    <el-date-picker v-model="dialogEditForm[fieldDescInfo.key]" type="datetime"
 | 
			
		||||
                                    placeholder="选个时间" format="YYYY/MM/DD HH:mm:ss"
 | 
			
		||||
                                    value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
 | 
			
		||||
                  </el-form-item>
 | 
			
		||||
                </template>
 | 
			
		||||
 | 
			
		||||
                <!-- 否则就是普通字段 -->
 | 
			
		||||
                <template v-else>
 | 
			
		||||
                  <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
                    <el-input v-model="dialogEditForm[fieldDescInfo.key]"
 | 
			
		||||
                              :placeholder="fieldDescInfo.help_text"></el-input>
 | 
			
		||||
                  </el-form-item>
 | 
			
		||||
                </template>
 | 
			
		||||
              </template>
 | 
			
		||||
 | 
			
		||||
              <template v-else>
 | 
			
		||||
                <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
                  <el-input v-model="dialogEditForm[fieldDescInfo.key]"
 | 
			
		||||
                            :placeholder="fieldDescInfo.help_text" disabled></el-input>
 | 
			
		||||
                </el-form-item>
 | 
			
		||||
              </template>
 | 
			
		||||
 | 
			
		||||
            </template>
 | 
			
		||||
 | 
			
		||||
            <!--            <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">-->
 | 
			
		||||
            <!--              <el-tooltip effect="light" :content="fieldDescInfo.help_text" placement="bottom-start"-->
 | 
			
		||||
            <!--                          v-if="(fieldDescInfo.type === 'items')">-->
 | 
			
		||||
            <!--                <el-select placeholder="可选项">-->
 | 
			
		||||
            <!--                  <el-option v-for="info in fieldDescInfo.choices" :key="info.desc" :label="info.desc"-->
 | 
			
		||||
            <!--                             :value="info.value"></el-option>-->
 | 
			
		||||
            <!--                </el-select>-->
 | 
			
		||||
            <!--              </el-tooltip>-->
 | 
			
		||||
            <!--              <el-input v-model="dialogAddForm[fieldDescInfo.key]" :placeholder="fieldDescInfo.name" v-else></el-input>-->
 | 
			
		||||
            <!--              <el-input v-model="dialogEditForm[fieldDescInfo.key]"></el-input>-->
 | 
			
		||||
            <!--            </el-form-item>-->
 | 
			
		||||
          </template>
 | 
			
		||||
 | 
			
		||||
          <el-button @click="submitAdd(dialogAddFormRef)" size="large" type="primary">提交</el-button>
 | 
			
		||||
 | 
			
		||||
        </el-form>
 | 
			
		||||
      </el-dialog>
 | 
			
		||||
 | 
			
		||||
      <el-dialog v-model="dialogEditVisible" :mask="true" title="编辑" :modal="true" :before-close="handleCloseDialog"
 | 
			
		||||
                 destroy-on-close>
 | 
			
		||||
        <el-form ref="dialogEditFormRef" :model="dialogEditForm" :rules="rules">
 | 
			
		||||
          <template v-for="fieldDescInfo in fieldsDescInfo">
 | 
			
		||||
            <el-form-item :label="fieldDescInfo.name" :prop="fieldDescInfo.key">
 | 
			
		||||
              <el-input v-model="dialogEditForm[fieldDescInfo.key]"></el-input>
 | 
			
		||||
            </el-form-item>
 | 
			
		||||
          </template>
 | 
			
		||||
 | 
			
		||||
          <el-form-item>
 | 
			
		||||
            <el-button @click="submitEdit(dialogEditFormRef)" size="large" type="primary">提交</el-button>
 | 
			
		||||
          </el-form-item>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import './assets/main.css'
 | 
			
		||||
// import './assets/main.css'
 | 
			
		||||
 | 
			
		||||
import {createApp} from 'vue'
 | 
			
		||||
import {createPinia} from 'pinia'
 | 
			
		||||
 | 
			
		||||
@ -28,11 +28,12 @@ router.beforeEach((to, from, next) => {
 | 
			
		||||
                projectList.value = res.data.projects
 | 
			
		||||
                setProjectOperationRoutes(projectList.value)
 | 
			
		||||
                // console.log("all routes:", router.getRoutes())
 | 
			
		||||
                next()
 | 
			
		||||
                next({...to, replace: true}); // 在已有页面里查找
 | 
			
		||||
            }, (err) => {
 | 
			
		||||
                console.log("跳转路径:", to.path, " 报错:", err)
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
            console.log("op tree routes length valid:", projectOpTreeRoutes.value.length)
 | 
			
		||||
            next()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import LocalCache from "@/stores/project.js";
 | 
			
		||||
const router = useRouter()
 | 
			
		||||
const route = useRoute()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// 计算当前激活的菜单项
 | 
			
		||||
const activeMenu = computed(() => route.path)
 | 
			
		||||
 | 
			
		||||
@ -22,15 +23,16 @@ const handleMenuSelect = (clickResource) => {
 | 
			
		||||
<template>
 | 
			
		||||
  <main>
 | 
			
		||||
    <div>
 | 
			
		||||
      <el-container style="height: 100vh;">
 | 
			
		||||
        <el-aside class="el-aside-demo" width="200px">
 | 
			
		||||
      <el-container class="layout-container">
 | 
			
		||||
        <el-aside class="el-aside-demo">
 | 
			
		||||
          <div class="sidebar-content">
 | 
			
		||||
            <!--          <el-avatar shape="square" :size="100" :src="avatarUrl"></el-avatar>-->
 | 
			
		||||
            <div class="sidebar-logo">
 | 
			
		||||
              <img src="@/assets/logo.svg" class="logo" alt="Logo">
 | 
			
		||||
              <span class="system-name">游戏后台管理系统</span>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
          <el-menu :default-active="activeMenu" class="el-menu-vertical-demo" :collapse="isCollapse">
 | 
			
		||||
            <el-menu :default-active="activeMenu" class="el-menu-vertical-demo" :collapse="false">
 | 
			
		||||
 | 
			
		||||
              <!-- 静态菜单 -->
 | 
			
		||||
              <el-menu-item index="/user" @click="$router.push('/user')">
 | 
			
		||||
@ -59,12 +61,38 @@ const handleMenuSelect = (clickResource) => {
 | 
			
		||||
 | 
			
		||||
                </el-sub-menu>
 | 
			
		||||
              </template>
 | 
			
		||||
 | 
			
		||||
            </el-menu>
 | 
			
		||||
          </div>
 | 
			
		||||
        </el-aside>
 | 
			
		||||
 | 
			
		||||
        <el-container class="el-container-right">
 | 
			
		||||
          <el-header style="border-bottom: #1f2d3d 1px solid">游戏后台管理系统</el-header>
 | 
			
		||||
          <el-main>
 | 
			
		||||
          <el-header class="el-header-demo">
 | 
			
		||||
            <div class="header-content">
 | 
			
		||||
              <div class="avatar-container">
 | 
			
		||||
                <el-dropdown class="right-menu-item hover-effect" trigger="click">
 | 
			
		||||
 | 
			
		||||
                  <!-- 头像 -->
 | 
			
		||||
                  <div class="avatar-wrapper">
 | 
			
		||||
                    <img :src="avatarUrl" class="user-avatar"/>
 | 
			
		||||
                    <el-icon color="black" size="20">
 | 
			
		||||
                      <ArrowDownBold/>
 | 
			
		||||
                    </el-icon>
 | 
			
		||||
                  </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                  <!-- 头像操作下拉菜单 -->
 | 
			
		||||
                  <template #dropdown>
 | 
			
		||||
                    <el-dropdown-menu>
 | 
			
		||||
                      <el-dropdown-item>个人中心</el-dropdown-item>
 | 
			
		||||
                      <el-dropdown-item>退出登录</el-dropdown-item>
 | 
			
		||||
                    </el-dropdown-menu>
 | 
			
		||||
                  </template>
 | 
			
		||||
                </el-dropdown>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <!--            <el-avatar shape="square" :size="100" :src="avatarUrl"></el-avatar>-->
 | 
			
		||||
          </el-header>
 | 
			
		||||
          <el-main class="el-main-demo">
 | 
			
		||||
            <router-view :key="$route.fullPath"></router-view>
 | 
			
		||||
          </el-main>
 | 
			
		||||
        </el-container>
 | 
			
		||||
@ -73,22 +101,7 @@ const handleMenuSelect = (clickResource) => {
 | 
			
		||||
  </main>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
 | 
			
		||||
.dashboard-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  /* 使用flex布局 */
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  /* 子元素之间的间隔 */
 | 
			
		||||
  align-items: flex-start;
 | 
			
		||||
  /* 子元素垂直方向顶部对齐 */
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
  max-width: 1200px;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  background-color: #71e4ae;
 | 
			
		||||
}
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
  color: #333;
 | 
			
		||||
@ -96,80 +109,46 @@ h1 {
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.info-list {
 | 
			
		||||
  list-style-type: none;
 | 
			
		||||
  /* 移除列表项前的标记 */
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
.layout-container {
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
 | 
			
		||||
.info-list li {
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
  color: #666;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar {
 | 
			
		||||
  .el-aside-demo {
 | 
			
		||||
    position: fixed;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    width: 200px;
 | 
			
		||||
  /* 侧边栏宽度 */
 | 
			
		||||
  background-color: #f4f4f4;
 | 
			
		||||
  /* 侧边栏背景色 */
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
 | 
			
		||||
    z-index: 1000;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
    box-shadow: 2px 0 6px rgba(0, 0, 0, 0.1);
 | 
			
		||||
    background-color: #4d4f52;
 | 
			
		||||
 | 
			
		||||
    .sidebar-content {
 | 
			
		||||
      overflow-y: auto;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
.sidebar ul {
 | 
			
		||||
  list-style-type: none;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar li {
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar a {
 | 
			
		||||
  color: #333;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  font-size: 16px;
 | 
			
		||||
  display: block;
 | 
			
		||||
  /* 使链接占据整行 */
 | 
			
		||||
  padding: 8px;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  transition: background-color 0.3s;
 | 
			
		||||
  /* 平滑过渡效果 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar a:hover {
 | 
			
		||||
  background-color: #c6ec97;
 | 
			
		||||
  /* 鼠标悬停时的背景色 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.main-content {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  /* 剩余空间 */
 | 
			
		||||
  padding: 20px;
 | 
			
		||||
  /* 内容区域的内边距 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sidebar-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  width: 200px;
 | 
			
		||||
  .el-container-right {
 | 
			
		||||
    min-height: 100vh;
 | 
			
		||||
  background-color: #545c64;
 | 
			
		||||
    margin-left: 200px;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
 | 
			
		||||
    .el-header-demo {
 | 
			
		||||
      height: 60px;
 | 
			
		||||
      background: #fff;
 | 
			
		||||
      border-bottom: 1px solid #e6e6e6;
 | 
			
		||||
      box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
.collapse-button {
 | 
			
		||||
  margin: 10px;
 | 
			
		||||
  align-self: flex-end;
 | 
			
		||||
  background-color: #545c64;
 | 
			
		||||
  color: #fff;
 | 
			
		||||
  border: none;
 | 
			
		||||
    .el-main-demo {
 | 
			
		||||
      flex: 1;
 | 
			
		||||
      padding: 20px;
 | 
			
		||||
      background: #f0f2f5;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.sidebar-logo {
 | 
			
		||||
  display: flex;
 | 
			
		||||
@ -179,7 +158,6 @@ h1 {
 | 
			
		||||
  background: transparent; /* 关键:透明背景 */
 | 
			
		||||
  position: relative;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .logo {
 | 
			
		||||
    width: 50px;
 | 
			
		||||
@ -196,23 +174,8 @@ h1 {
 | 
			
		||||
    letter-spacing: 1px;
 | 
			
		||||
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2); /* 文字阴影增强可读性 */
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
.el-aside-demo {
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  width: 200px;
 | 
			
		||||
  z-index: 1000;
 | 
			
		||||
  overflow-y: auto;
 | 
			
		||||
  background-color: #4d4f52;
 | 
			
		||||
  border-right: 1px solid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-container-right {
 | 
			
		||||
  margin-left: 0;
 | 
			
		||||
  width: calc(100vw - 220px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-menu-vertical-demo {
 | 
			
		||||
  flex: 1;
 | 
			
		||||
@ -244,8 +207,35 @@ h1 {
 | 
			
		||||
  margin-right: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.router-link-active {
 | 
			
		||||
  color: #ffd04b;
 | 
			
		||||
.avatar-container {
 | 
			
		||||
  height: 50px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  float: right;
 | 
			
		||||
  background: #fff;
 | 
			
		||||
  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
 | 
			
		||||
 | 
			
		||||
  margin-right: 40px;
 | 
			
		||||
 | 
			
		||||
  .avatar-wrapper {
 | 
			
		||||
    margin-top: 5px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
    .user-avatar {
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      width: 40px;
 | 
			
		||||
      height: 40px;
 | 
			
		||||
      border-radius: 10px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    i {
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      right: -20px;
 | 
			
		||||
      top: 25px;
 | 
			
		||||
      font-size: 12px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user