From 8227a73e35711a942f2c1d664664b0d88615d9f2 Mon Sep 17 00:00:00 2001 From: RockYang Date: Fri, 29 Mar 2024 15:41:58 +0800 Subject: [PATCH] feat: support custom menu --- api/core/app_server.go | 1 + api/core/types/config.go | 10 +- api/handler/admin/menu_handler.go | 121 +++++++++++++ api/handler/admin/product_handler.go | 18 +- api/handler/chat_role_handler.go | 6 +- api/handler/menu_handler.go | 36 ++++ api/handler/sd_handler.go | 22 ++- api/main.go | 14 ++ api/service/mj/pool.go | 1 + api/service/mj/service.go | 18 +- api/service/sd/service.go | 1 + api/store/model/menu.go | 11 ++ api/store/vo/menu.go | 11 ++ database/update-v4.0.2.sql | 19 ++ web/public/images/menu/app.png | Bin 0 -> 741 bytes web/public/images/{ => menu}/chat.png | Bin web/public/images/menu/img-wall.png | Bin 0 -> 1196 bytes web/public/images/menu/log.png | Bin 0 -> 939 bytes web/public/images/menu/member.png | Bin 0 -> 1774 bytes web/public/images/{ => menu}/mj.png | Bin web/public/images/{ => menu}/sd.png | Bin web/public/images/menu/share.png | Bin 0 -> 1238 bytes web/src/router.js | 2 +- web/src/views/ChatPlus.vue | 14 +- web/src/views/Home.vue | 26 ++- web/src/views/admin/Menu.vue | 247 ++++++++++++++++++++++++++ web/src/views/admin/Product.vue | 8 +- web/src/views/admin/SysConfig.vue | 5 + 28 files changed, 532 insertions(+), 59 deletions(-) create mode 100644 api/handler/admin/menu_handler.go create mode 100644 api/handler/menu_handler.go create mode 100644 api/store/model/menu.go create mode 100644 api/store/vo/menu.go create mode 100644 database/update-v4.0.2.sql create mode 100644 web/public/images/menu/app.png rename web/public/images/{ => menu}/chat.png (100%) create mode 100644 web/public/images/menu/img-wall.png create mode 100644 web/public/images/menu/log.png create mode 100644 web/public/images/menu/member.png rename web/public/images/{ => menu}/mj.png (100%) rename web/public/images/{ => menu}/sd.png (100%) create mode 100644 web/public/images/menu/share.png create mode 100644 web/src/views/admin/Menu.vue diff --git a/api/core/app_server.go b/api/core/app_server.go index d633fc18..2fca4ea7 100644 --- a/api/core/app_server.go +++ b/api/core/app_server.go @@ -217,6 +217,7 @@ func needLogin(c *gin.Context) bool { c.Request.URL.Path == "/api/sd/client" || c.Request.URL.Path == "/api/config/get" || c.Request.URL.Path == "/api/product/list" || + c.Request.URL.Path == "/api/menu/list" || strings.HasPrefix(c.Request.URL.Path, "/api/test") || strings.HasPrefix(c.Request.URL.Path, "/api/function/") || strings.HasPrefix(c.Request.URL.Path, "/api/sms/") || diff --git a/api/core/types/config.go b/api/core/types/config.go index 324c3cad..24a94e78 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -144,13 +144,13 @@ type SystemConfig struct { PowerPrice float64 `json:"power_price,omitempty"` // 算力单价 OrderPayTimeout int `json:"order_pay_timeout,omitempty"` //订单支付超时时间 - VipInfoText string `json:"vip_info_text"` // 会员页面充值说明 + VipInfoText string `json:"vip_info_text,omitempty"` // 会员页面充值说明 DefaultModels []int `json:"default_models,omitempty"` // 默认开通的 AI 模型 - MjPower int `json:"mj_power,omitempty"` // MJ 绘画消耗算力 - MjActionPower int `json:"mj_action_power"` // MJ 操作(放大,变换)消耗算力 - SdPower int `json:"sd_power,omitempty"` // SD 绘画消耗算力 - DallPower int `json:"dall_power,omitempty"` // DALLE3 绘图消耗算力 + MjPower int `json:"mj_power,omitempty"` // MJ 绘画消耗算力 + MjActionPower int `json:"mj_action_power,omitempty"` // MJ 操作(放大,变换)消耗算力 + SdPower int `json:"sd_power,omitempty"` // SD 绘画消耗算力 + DallPower int `json:"dall_power,omitempty"` // DALLE3 绘图消耗算力 WechatCardURL string `json:"wechat_card_url,omitempty"` // 微信客服地址 diff --git a/api/handler/admin/menu_handler.go b/api/handler/admin/menu_handler.go new file mode 100644 index 00000000..7358ffa9 --- /dev/null +++ b/api/handler/admin/menu_handler.go @@ -0,0 +1,121 @@ +package admin + +import ( + "chatplus/core" + "chatplus/core/types" + "chatplus/handler" + "chatplus/store/model" + "chatplus/store/vo" + "chatplus/utils" + "chatplus/utils/resp" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type MenuHandler struct { + handler.BaseHandler +} + +func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler { + return &MenuHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} +} + +func (h *MenuHandler) Save(c *gin.Context) { + var data struct { + Id uint `json:"id"` + Name string `json:"name"` + Icon string `json:"icon"` + URL string `json:"url"` + SortNum int `json:"sort_num"` + Enabled bool `json:"enabled"` + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + res := h.DB.Save(&model.Menu{ + Id: data.Id, + Name: data.Name, + Icon: data.Icon, + URL: data.URL, + SortNum: data.SortNum, + Enabled: data.Enabled, + }) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } + resp.SUCCESS(c) +} + +// List 数据列表 +func (h *MenuHandler) List(c *gin.Context) { + var items []model.Menu + var list = make([]vo.Menu, 0) + res := h.DB.Order("sort_num ASC").Find(&items) + if res.Error == nil { + for _, item := range items { + var product vo.Menu + err := utils.CopyObject(item, &product) + if err == nil { + list = append(list, product) + } + } + } + resp.SUCCESS(c, list) +} + +func (h *MenuHandler) Enable(c *gin.Context) { + var data struct { + Id uint `json:"id"` + Enabled bool `json:"enabled"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + res := h.DB.Model(&model.Menu{}).Where("id", data.Id).UpdateColumn("enabled", data.Enabled) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } + resp.SUCCESS(c) +} + +func (h *MenuHandler) Sort(c *gin.Context) { + var data struct { + Ids []uint `json:"ids"` + Sorts []int `json:"sorts"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + for index, id := range data.Ids { + res := h.DB.Model(&model.Menu{}).Where("id", id).Update("sort_num", data.Sorts[index]) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } + } + + resp.SUCCESS(c) +} + +func (h *MenuHandler) Remove(c *gin.Context) { + id := h.GetInt(c, "id", 0) + + if id > 0 { + res := h.DB.Where("id", id).Delete(&model.Menu{}) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } + } + resp.SUCCESS(c) +} diff --git a/api/handler/admin/product_handler.go b/api/handler/admin/product_handler.go index 8e960c24..42eff03f 100644 --- a/api/handler/admin/product_handler.go +++ b/api/handler/admin/product_handler.go @@ -65,21 +65,11 @@ func (h *ProductHandler) Save(c *gin.Context) { resp.SUCCESS(c, itemVo) } -// List 模型列表 +// List 数据列表 func (h *ProductHandler) List(c *gin.Context) { - if err := utils.CheckPermission(c, h.DB); err != nil { - resp.NotPermission(c) - return - } - - session := h.DB.Session(&gorm.Session{}) - enable := h.GetBool(c, "enable") - if enable { - session = session.Where("enabled", enable) - } var items []model.Product var list = make([]vo.Product, 0) - res := session.Order("sort_num ASC").Find(&items) + res := h.DB.Order("sort_num ASC").Find(&items) if res.Error == nil { for _, item := range items { var product vo.Product @@ -128,7 +118,7 @@ func (h *ProductHandler) Sort(c *gin.Context) { } for index, id := range data.Ids { - res := h.DB.Model(&model.Product{}).Where("id = ?", id).Update("sort_num", data.Sorts[index]) + res := h.DB.Model(&model.Product{}).Where("id", id).Update("sort_num", data.Sorts[index]) if res.Error != nil { resp.ERROR(c, "更新数据库失败!") return @@ -142,7 +132,7 @@ func (h *ProductHandler) Remove(c *gin.Context) { id := h.GetInt(c, "id", 0) if id > 0 { - res := h.DB.Where("id = ?", id).Delete(&model.Product{}) + res := h.DB.Where("id", id).Delete(&model.Product{}) if res.Error != nil { resp.ERROR(c, "更新数据库失败!") return diff --git a/api/handler/chat_role_handler.go b/api/handler/chat_role_handler.go index 4e48a60b..a555256d 100644 --- a/api/handler/chat_role_handler.go +++ b/api/handler/chat_role_handler.go @@ -25,9 +25,10 @@ func (h *ChatRoleHandler) List(c *gin.Context) { all := h.GetBool(c, "all") userId := h.GetLoginUserId(c) var roles []model.ChatRole + var roleVos = make([]vo.ChatRole, 0) res := h.DB.Where("enable", true).Order("sort_num ASC").Find(&roles) if res.Error != nil { - resp.ERROR(c, "No roles found,"+res.Error.Error()) + resp.SUCCESS(c, roleVos) return } @@ -55,8 +56,7 @@ func (h *ChatRoleHandler) List(c *gin.Context) { resp.ERROR(c, "角色解析失败!") return } - // 转成 vo - var roleVos = make([]vo.ChatRole, 0) + for _, r := range roles { if !utils.ContainsStr(roleKeys, r.Key) { continue diff --git a/api/handler/menu_handler.go b/api/handler/menu_handler.go new file mode 100644 index 00000000..b922f54c --- /dev/null +++ b/api/handler/menu_handler.go @@ -0,0 +1,36 @@ +package handler + +import ( + "chatplus/core" + "chatplus/store/model" + "chatplus/store/vo" + "chatplus/utils" + "chatplus/utils/resp" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type MenuHandler struct { + BaseHandler +} + +func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler { + return &MenuHandler{BaseHandler: BaseHandler{App: app, DB: db}} +} + +// List 数据列表 +func (h *MenuHandler) List(c *gin.Context) { + var items []model.Menu + var list = make([]vo.Menu, 0) + res := h.DB.Where("enabled", true).Order("sort_num ASC").Find(&items) + if res.Error == nil { + for _, item := range items { + var product vo.Menu + err := utils.CopyObject(item, &product) + if err == nil { + list = append(list, product) + } + } + } + resp.SUCCESS(c, list) +} diff --git a/api/handler/sd_handler.go b/api/handler/sd_handler.go index 9881a35d..09b35d49 100644 --- a/api/handler/sd_handler.go +++ b/api/handler/sd_handler.go @@ -3,6 +3,7 @@ package handler import ( "chatplus/core" "chatplus/core/types" + "chatplus/service" "chatplus/service/oss" "chatplus/service/sd" "chatplus/store/model" @@ -23,15 +24,17 @@ import ( type SdJobHandler struct { BaseHandler - redis *redis.Client - pool *sd.ServicePool - uploader *oss.UploaderManager + redis *redis.Client + pool *sd.ServicePool + uploader *oss.UploaderManager + snowflake *service.Snowflake } -func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool, manager *oss.UploaderManager) *SdJobHandler { +func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool, manager *oss.UploaderManager, snowflake *service.Snowflake) *SdJobHandler { return &SdJobHandler{ - pool: pool, - uploader: manager, + pool: pool, + uploader: manager, + snowflake: snowflake, BaseHandler: BaseHandler{ App: app, DB: db, @@ -116,8 +119,13 @@ func (h *SdJobHandler) Image(c *gin.Context) { } idValue, _ := c.Get(types.LoginUserID) userId := utils.IntValue(utils.InterfaceToString(idValue), 0) + taskId, err := h.snowflake.Next(true) + if err != nil { + resp.ERROR(c, "error with generate task id: "+err.Error()) + return + } params := types.SdTaskParams{ - TaskId: fmt.Sprintf("task(%s)", utils.RandString(15)), + TaskId: taskId, Prompt: data.Prompt, NegativePrompt: data.NegativePrompt, Steps: data.Steps, diff --git a/api/main.go b/api/main.go index c5ce4baa..6d82fccb 100644 --- a/api/main.go +++ b/api/main.go @@ -417,6 +417,20 @@ func main() { group := s.Engine.Group("/api/admin/powerLog/") group.POST("list", h.List) }), + fx.Provide(admin.NewMenuHandler), + fx.Invoke(func(s *core.AppServer, h *admin.MenuHandler) { + group := s.Engine.Group("/api/admin/menu/") + group.POST("save", h.Save) + group.GET("list", h.List) + group.POST("enable", h.Enable) + group.POST("sort", h.Sort) + group.GET("remove", h.Remove) + }), + fx.Provide(handler.NewMenuHandler), + fx.Invoke(func(s *core.AppServer, h *handler.MenuHandler) { + group := s.Engine.Group("/api/menu/") + group.GET("list", h.List) + }), fx.Invoke(func(s *core.AppServer, db *gorm.DB) { err := s.Run(db) if err != nil { diff --git a/api/service/mj/pool.go b/api/service/mj/pool.go index 14ea1da4..0143467f 100644 --- a/api/service/mj/pool.go +++ b/api/service/mj/pool.go @@ -181,6 +181,7 @@ func (p *ServicePool) SyncTaskProgress() { CreatedAt: time.Now(), }) } + continue } if servicePlus := p.getService(job.ChannelId); servicePlus != nil { diff --git a/api/service/mj/service.go b/api/service/mj/service.go index 861095d4..f73a9c63 100644 --- a/api/service/mj/service.go +++ b/api/service/mj/service.go @@ -77,6 +77,13 @@ func (s *Service) Run() { } } + var job model.MidJourneyJob + tx := s.db.Where("id = ?", task.Id).First(&job) + if tx.Error != nil { + logger.Error("任务不存在,任务ID:", task.TaskId) + continue + } + logger.Infof("%s handle a new MidJourney task: %+v", s.Name, task) var res ImageRes switch task.Type { @@ -97,8 +104,6 @@ func (s *Service) Run() { break } - var job model.MidJourneyJob - s.db.Where("id = ?", task.Id).First(&job) if err != nil || (res.Code != 1 && res.Code != 22) { errMsg := fmt.Sprintf("%v,%s", err, res.Description) logger.Error("绘画任务执行失败:", errMsg) @@ -127,14 +132,17 @@ func (s *Service) canHandleTask() bool { return handledNum < s.maxHandleTaskNum } -// remove the expired tasks +// remove the timeout tasks func (s *Service) checkTasks() { for k, t := range s.taskStartTimes { if time.Now().Unix()-t.Unix() > s.taskTimeout { delete(s.taskStartTimes, k) atomic.AddInt32(&s.HandledTaskNum, -1) - // delete task from database - s.db.Delete(&model.MidJourneyJob{Id: uint(k)}, "progress < 100") + + s.db.Model(&model.MidJourneyJob{Id: uint(k)}).UpdateColumns(map[string]interface{}{ + "progress": -1, + "err_msg": "任务超时", + }) } } } diff --git a/api/service/sd/service.go b/api/service/sd/service.go index ccb56974..f87067a3 100644 --- a/api/service/sd/service.go +++ b/api/service/sd/service.go @@ -114,6 +114,7 @@ func (s *Service) Txt2Img(task types.SdTask) error { Width: task.Params.Width, Height: task.Params.Height, SamplerName: task.Params.Sampler, + ForceTaskId: task.Params.TaskId, } if task.Params.Seed > 0 { body.Seed = task.Params.Seed diff --git a/api/store/model/menu.go b/api/store/model/menu.go new file mode 100644 index 00000000..e215e204 --- /dev/null +++ b/api/store/model/menu.go @@ -0,0 +1,11 @@ +package model + +// Menu 系统菜单 +type Menu struct { + Id uint `gorm:"primarykey;column:id"` + Name string // 菜单名称 + Icon string // 菜单图标 + URL string // 菜单跳转地址 + SortNum int // 排序 + Enabled bool // 启用状态 +} diff --git a/api/store/vo/menu.go b/api/store/vo/menu.go new file mode 100644 index 00000000..c9975a45 --- /dev/null +++ b/api/store/vo/menu.go @@ -0,0 +1,11 @@ +package vo + +// Menu 系统菜单 +type Menu struct { + Id uint `json:"id"` + Name string `json:"name"` + Icon string `json:"icon"` + URL string `json:"url"` + SortNum int `json:"sort_num"` + Enabled bool `json:"enabled"` +} diff --git a/database/update-v4.0.2.sql b/database/update-v4.0.2.sql new file mode 100644 index 00000000..00d7708f --- /dev/null +++ b/database/update-v4.0.2.sql @@ -0,0 +1,19 @@ +-- 菜单表 +CREATE TABLE `chatgpt_plus`.`chatgpt_menus` ( + `id` INT(11) NOT NULL AUTO_INCREMENT , + `name` VARCHAR(30) NOT NULL COMMENT '菜单名称' , + `icon` VARCHAR(150) NOT NULL COMMENT '菜单图标' , + `url` VARCHAR(100) NOT NULL COMMENT '地址' , + `sort_num` SMALLINT(3) NOT NULL COMMENT '排序' , + `enabled` TINYINT(1) NOT NULL COMMENT '是否启用' , + PRIMARY KEY (`id`)) ENGINE = InnoDB COMMENT = '前端菜单表'; + +INSERT INTO `chatgpt_menus` (`id`, `name`, `icon`, `url`, `sort_num`, `enabled`) VALUES + (1, '对话聊天', '/images/menu/chat.png', '/chat', 0, 1), + (5, 'MJ 绘画', '/images/menu/mj.png', '/mj', 1, 1), + (6, 'SD 绘画', '/images/menu/sd.png', '/sd', 2, 1), + (7, '算力日志', '/images/menu/log.png', '/powerLog', 5, 1), + (8, '应用中心', '/images/menu/app.png', '/apps', 3, 1), + (9, '作品展示', '/images/menu/img-wall.png', '/images-wall', 4, 1), + (10, '会员计划', '/images/menu/member.png', '/member', 6, 1), + (11, '分享计划', '/images/menu/share.png', '/invite', 7, 1); \ No newline at end of file diff --git a/web/public/images/menu/app.png b/web/public/images/menu/app.png new file mode 100644 index 0000000000000000000000000000000000000000..2785231cbe4a00d7541899a7459fa78d324842c7 GIT binary patch literal 741 zcmVPx%pGibPRA@u(Sv_bJQ4s#-&NLRgUPQ1+A)=Qm! z?Fokbk|EC@mcXDpxVbyvvv!t%4-|ImR9b+a1l}rfW|Hj7{4pyi?guccg9qRv(fCxA z(rlW2Y6n`u3fDCQ#mofwRuOkc_m3XyPXM0Eg9pfp5@#aGH<5tHzz49DFIdZqkVjQ< z&PAV1iqXk|2yY~HB85QxHoz+;1R~!NjEpVH)-N7|R{##0;mKnq&Y8b4(g?f&c+@N) zAP6TaQd;pC&=f0ej4Xj(m9-s+@3zEEWJk!3koI~?KFsV0*%8`^5h}AbBaoZg*y)CQ zmALPH)}InQX^3}EiF-loQNm|%n7~WJfX|e;Cn5}=!6gEB4e=(GxDT`l^x+Bec>|4$ z%QE`86VPXHpTJ}e62FLWE=?2kRAfHCA0W0IAsS;kfm?#6xK%nKN`toP*?~w&0flja?9@w?Uy1Nt+PwZ(P3$p0GO14dRC+x8{ z56Dyw5+<5BI8N|1P-u>V#Px(W=TXrRA@u(SzU-5RTTcdi9fBdnS`Z6`yyf=6suqt)CVi6m6^1wK3HF>R9mqX zgg*F(rG01>WlLdw5XB#?qDc2)%VMqW=8j?)mD-j{tx!P_tokVKW~Nj{cITLzWXvQ@ zvdLr;-IDt@_uO;t_nmX@J!fW!MLxL5@uN@Rzm8x(0{sYd=z)t;$dcK;9op=wy2Er8 zzA6H?ct-)rbQM9z#yPE+TpO6FA>c~K_IqGJ(fk+prAt8IKVO?~J#cQ_EwVD35g11U zVM`IXWC>W}?l8br0G12zy}%!LnEIxh(TObrw$wl5gugb&^mMmK#6ln|vs(nlyY)d< zUu|&{iG_eI?)v~XYjE1*#ARoF$Qyv%ujd5V;Lx0k8@#p`3xTY79|`1MZP0*m^%Mtw zv&7p^$g?E^^0>p)TmM@Ews`vhd9DPI6M#Ph7%PLyP59U5m_FAkfpW|8t;6((3+coX z0ZZ~j1Wz^CY)aAGun>?yZj1XcfcFH*6MjZ9eWsaiECedKnAj8Um)fltws>m_`ufH) z%Aijhn9EFY&}&2@HUhy0mbmKytR$d4|6}W(zpxyb`x3x))jFI|Om6HMfmQ+6uCpco zDZslM=)bIJK3z{1YXq|5jsZNAV{%_RM6%+&DUj_Uoksv}t!dwPD4MU<+Kr8XE$)i| zc9&`aHs@%5&GXzp1r=a>2LkLh#_$?j1!KMC87g@7%yTLI&pkl}fO zahJn^FPf3CCBG5iq0pLxisnO=8!5jUpgzxaAB#+Y zNUQ*4TVPOt+Z~!G^mBD>U{MN!{alBODisJ(D_fU?@eux4- z(6PlGE=1ulAc?S@mIw$W56llc9MqL^!C^`M0fOUtA%Wo>O`XRb4@Oc? zEODPSfcYHFS2{)>GGI%77r>X=I>hbv`EdfSwH2ujl>3uz;%d`&<)d>;XP`~%Jzd^} zz#{*=uHCq(e{l3P4`SM^|Ccf*M@BZ&k3eMC#iZDeKunH|Y~~+zw|!53tARfN0000< KMNUMnLSTZ3DL)MW literal 0 HcmV?d00001 diff --git a/web/public/images/menu/log.png b/web/public/images/menu/log.png new file mode 100644 index 0000000000000000000000000000000000000000..03870d66ce8ee24fa15bcde8c922b974bf96f081 GIT binary patch literal 939 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 Qp8x;=07*qoM6N<$f@=aW{Qv*} literal 0 HcmV?d00001 diff --git a/web/public/images/mj.png b/web/public/images/menu/mj.png similarity index 100% rename from web/public/images/mj.png rename to web/public/images/menu/mj.png diff --git a/web/public/images/sd.png b/web/public/images/menu/sd.png similarity index 100% rename from web/public/images/sd.png rename to web/public/images/menu/sd.png diff --git a/web/public/images/menu/share.png b/web/public/images/menu/share.png new file mode 100644 index 0000000000000000000000000000000000000000..8d84b07425b58c728cfa2e32e978dffddfe03295 GIT binary patch literal 1238 zcmV;{1S$K8P)Px(kV!;ARA@u(nO|reRTRd5XSS#Xvy;S@V)a3>sBNjW!3v6E!MM9gK@qDh`l6&N zRY6ceQw39LQ;}jV#QG%hMJ;Ic!M52+6bqt7q4=lRScoFjH``=qlc*)xIp$_}lFgXS z{>g5a0p}^1d+yoqyXV|h#dhW!uPcF=j&upus{vrY=l;JY|Gnbv>X)XjAc?vW zNa*lQO5{f$kfNO)-qFvc*`yAwI=s%p+APRk-$ux+PfGJo`6TK>;6c@;X-1EMZZcpT zu@}Sp%}X(zvD0 z1l&Ww)({g&?F88q;uLZ>14l0UCcaz8RxDxis&m+k9;ttT%d)9dgM%!l?oomEVk<4^ z&ZIQXoS6Cc33WFr@&xGiy!T@QZ+T?C6(VosM*~>Al!)udda!<&3tWCyI6VdA4?#A7 zt_7C+HbJd&kB;4JF}V%o#lQme2-1_3=o$0AUDMZ3qWc$EsPe^JT-`r`Wz&Z@{HA0c zb)@;LU?dWn?o_mUwkGRC9^N2COo@D4U`1ZNKBTbU5&OIOBHE_o8!}9^2-Y2-ODmSs z0fqRzBX-Ne$9$?4WMDQD3`*Vx73Av--fIKW*k3FaD;b|7qT)K%3K<{F$R&(l{h6%% ztC-`srVfJam}{qJ73u63+t)nB+*C9^hd@kII|MnH^<-^vq~UO>USsMyeuc>bkG@{& z+{M@hB9Pt(`n+*Ci7fj9@BKUpq<%$<6WDSCcm1L>Y^gMIRp}#{zTFHK?WU( zt|`Z3OjBD0F&f7Zcq}PR@0ByZQrj94knKr{zFR4%a^}?}g)FSKebR8c93Yk2)?^b4 za(hyuCn^P1&b%6yQF<;gzU#QGJXa2&a&7-}nO^Kc7632|-V2U2^_B~)RQq|m0hLM# zqlYZ4Aw@d;jzbQ}fQ9bsD{vZDy)##dm;;iI*rq*5n~pA5w8KbbAey(D2O!ex$mKKr zAk&!xb}QUcVAabPbH1Ze<+KrL{bD~C1Q6F$i^5Yf9TET0vr{7P9g8KslVqNpN`YXv zZSitCA4q`l@e*K{W~n!jt>nJZr5E3+`2Iz_*^s0UePPz%or z^+!IRR;WL-`Xs6@r>mVRwe(k)&#cuX6nX;x0t?!IPcSv*qyPW_07*qoM6N<$f|_|t A*Z=?k literal 0 HcmV?d00001 diff --git a/web/src/router.js b/web/src/router.js index f4c81e61..968b0690 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -173,7 +173,7 @@ const routes = [ name: 'admin-manger', meta: {title: '管理员'}, component: () => import('@/views/admin/Manager.vue'), - }, + } ] }, diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index 19a729a6..0914358f 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -313,12 +313,16 @@ httpGet("/api/config/get?key=system").then(res => { // 获取系统公告 httpGet("/api/config/get?key=notice").then(res => { - notice.value = md.render(res.data['content']) - const oldNotice = localStorage.getItem(noticeKey.value); - // 如果公告有更新,则显示公告 - if (oldNotice !== notice.value && notice.value.length > 10) { - showNotice.value = true + try { + notice.value = md.render(res.data['content']) + const oldNotice = localStorage.getItem(noticeKey.value); + // 如果公告有更新,则显示公告 + if (oldNotice !== notice.value && notice.value.length > 10) { + showNotice.value = true + } + } catch (e) { } + }).catch(e => { ElMessage.error("获取系统配置失败:" + e.message) }) diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue index db214038..43687e56 100644 --- a/web/src/views/Home.vue +++ b/web/src/views/Home.vue @@ -11,10 +11,9 @@ - - + -
{{ item.title }}
+
{{ item.name }}
@@ -37,21 +36,12 @@ import {ElMessage} from "element-plus"; const router = useRouter(); const logo = ref('/images/logo.png'); -const navs = ref([ - {path: "/chat", icon_path: "/images/chat.png", title: "对话聊天"}, - {path: "/mj", icon_path: "/images/mj.png", title: "MJ 绘画"}, - {path: "/sd", icon_path: "/images/sd.png", title: "SD 绘画"}, - {path: "/apps", icon: "menu", title: "应用中心"}, - {path: "/images-wall", icon: "image-list", title: "作品展示"}, - {path: "/powerLog", icon: "log", title: "消费日志"}, - {path: "/member", icon: "vip-user", title: "会员计划"}, - {path: "/invite", icon: "share", title: "推广计划"}, -]) +const navs = ref([]) const curPath = ref(router.currentRoute.value.path) const changeNav = (item) => { - curPath.value = item.path - router.push(item.path) + curPath.value = item.url + router.push(item.url) } onMounted(() => { @@ -60,6 +50,12 @@ onMounted(() => { }).catch(e => { ElMessage.error("获取系统配置失败:" + e.message) }) + // 获取菜单 + httpGet("/api/menu/list").then(res => { + navs.value = res.data + }).catch(e => { + ElMessage.error("获取系统菜单失败:" + e.message) + }) }) diff --git a/web/src/views/admin/Menu.vue b/web/src/views/admin/Menu.vue new file mode 100644 index 00000000..66123e1d --- /dev/null +++ b/web/src/views/admin/Menu.vue @@ -0,0 +1,247 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/admin/Product.vue b/web/src/views/admin/Product.vue index 3f4bd8a5..eae3e812 100644 --- a/web/src/views/admin/Product.vue +++ b/web/src/views/admin/Product.vue @@ -1,5 +1,5 @@