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 ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar-content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
.el-main-demo {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-button {
|
||||
margin: 10px;
|
||||
align-self: flex-end;
|
||||
background-color: #545c64;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.sidebar-logo {
|
||||
display: flex;
|
||||
@ -179,40 +158,24 @@ h1 {
|
||||
background: transparent; /* 关键:透明背景 */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.logo {
|
||||
.logo {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin-bottom: 10px;
|
||||
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.5)); /* 添加微光效果 */
|
||||
}
|
||||
}
|
||||
|
||||
.system-name {
|
||||
.system-name {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
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