mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-18 01:06:39 +08:00
enable to update AI Drawing configuarations in admin console page
This commit is contained in:
parent
1a4d798f8b
commit
e35c34ad9a
@ -6,6 +6,8 @@
|
|||||||
* 功能新增:给思维导图增加 ToolBar,实现思维导图的放大缩小和定位
|
* 功能新增:给思维导图增加 ToolBar,实现思维导图的放大缩小和定位
|
||||||
* Bug修复:修复思维导图不扣费的Bug
|
* Bug修复:修复思维导图不扣费的Bug
|
||||||
* Bug修复:修复管理后台角色删除失败的Bug
|
* Bug修复:修复管理后台角色删除失败的Bug
|
||||||
|
* Bug修复:兼容最新版秋叶SD懒人包的 SD API,新增 scheduler 参数
|
||||||
|
* 功能优化:支持在管理后台配置 AI 绘图相关配置,包括 SD, MJ-PLUS, MJ-PROXY
|
||||||
|
|
||||||
## v4.0.5
|
## v4.0.5
|
||||||
|
|
||||||
@ -45,6 +47,7 @@
|
|||||||
* 功能新增:支持管理后台 Logo 修改
|
* 功能新增:支持管理后台 Logo 修改
|
||||||
|
|
||||||
## 4.0.2
|
## 4.0.2
|
||||||
|
|
||||||
* 功能新增:支持前端菜单可以配置
|
* 功能新增:支持前端菜单可以配置
|
||||||
* 功能优化:在登录和注册界面标题显示软件版本号
|
* 功能优化:在登录和注册界面标题显示软件版本号
|
||||||
* 功能优化:MJ 绘画支持 --sref 和 --cref 图片一致性参数
|
* 功能优化:MJ 绘画支持 --sref 和 --cref 图片一致性参数
|
||||||
@ -54,6 +57,7 @@
|
|||||||
* 功能新增:管理后台登录页面增加行为验证码,防止爆破
|
* 功能新增:管理后台登录页面增加行为验证码,防止爆破
|
||||||
|
|
||||||
## v4.0.1
|
## v4.0.1
|
||||||
|
|
||||||
* 功能重构:重构 Stable-Diffusion 绘画实现,使用 SDAPI 替换之前的 websocket 接口,SDAPI 兼容各种 stable-diffusion
|
* 功能重构:重构 Stable-Diffusion 绘画实现,使用 SDAPI 替换之前的 websocket 接口,SDAPI 兼容各种 stable-diffusion
|
||||||
发行版,稳定性更强一些
|
发行版,稳定性更强一些
|
||||||
* 功能优化:使用 [midjouney-proxy](https://github.com/novicezk/midjourney-proxy) 项目替换内置的原生 MidJourney API,兼容
|
* 功能优化:使用 [midjouney-proxy](https://github.com/novicezk/midjourney-proxy) 项目替换内置的原生 MidJourney API,兼容
|
||||||
@ -63,6 +67,7 @@
|
|||||||
* Bug修复:修复手机端 MidJourney 绘画页面滚动条无法滚动的Bug
|
* Bug修复:修复手机端 MidJourney 绘画页面滚动条无法滚动的Bug
|
||||||
|
|
||||||
## v4.0.0
|
## v4.0.0
|
||||||
|
|
||||||
非兼容版本,重大重构,引入算力概念,将系统中所有的能力(AI对话,MJ绘画,SD绘画,DALL绘画)全部使用算力来兑换。
|
非兼容版本,重大重构,引入算力概念,将系统中所有的能力(AI对话,MJ绘画,SD绘画,DALL绘画)全部使用算力来兑换。
|
||||||
只要你的算力值余额不为0,你就可以进行任何操作。比如一次 GPT3.5 对话消耗1个单位算力,一次 GPT4 对话消耗10个算力。一次 MJ
|
只要你的算力值余额不为0,你就可以进行任何操作。比如一次 GPT3.5 对话消耗1个单位算力,一次 GPT4 对话消耗10个算力。一次 MJ
|
||||||
对话消耗15个算力...
|
对话消耗15个算力...
|
||||||
|
@ -55,9 +55,10 @@ type SdTaskParams struct {
|
|||||||
NegPrompt string `json:"neg_prompt"` // 反向提示词
|
NegPrompt string `json:"neg_prompt"` // 反向提示词
|
||||||
Steps int `json:"steps"` // 迭代步数,默认20
|
Steps int `json:"steps"` // 迭代步数,默认20
|
||||||
Sampler string `json:"sampler"` // 采样器
|
Sampler string `json:"sampler"` // 采样器
|
||||||
FaceFix bool `json:"face_fix"` // 面部修复
|
Scheduler string `json:"scheduler"`
|
||||||
CfgScale float32 `json:"cfg_scale"` //引导系数,默认 7
|
FaceFix bool `json:"face_fix"` // 面部修复
|
||||||
Seed int64 `json:"seed"` // 随机数种子
|
CfgScale float32 `json:"cfg_scale"` //引导系数,默认 7
|
||||||
|
Seed int64 `json:"seed"` // 随机数种子
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
Width int `json:"width"`
|
Width int `json:"width"`
|
||||||
HdFix bool `json:"hd_fix"` // 启用高清修复
|
HdFix bool `json:"hd_fix"` // 启用高清修复
|
||||||
|
@ -12,6 +12,8 @@ import (
|
|||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/handler"
|
"geekai/handler"
|
||||||
"geekai/service"
|
"geekai/service"
|
||||||
|
"geekai/service/mj"
|
||||||
|
"geekai/service/sd"
|
||||||
"geekai/store"
|
"geekai/store"
|
||||||
"geekai/store/model"
|
"geekai/store/model"
|
||||||
"geekai/utils"
|
"geekai/utils"
|
||||||
@ -26,10 +28,18 @@ type ConfigHandler struct {
|
|||||||
handler.BaseHandler
|
handler.BaseHandler
|
||||||
levelDB *store.LevelDB
|
levelDB *store.LevelDB
|
||||||
licenseService *service.LicenseService
|
licenseService *service.LicenseService
|
||||||
|
mjServicePool *mj.ServicePool
|
||||||
|
sdServicePool *sd.ServicePool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigHandler(app *core.AppServer, db *gorm.DB, levelDB *store.LevelDB, licenseService *service.LicenseService) *ConfigHandler {
|
func NewConfigHandler(app *core.AppServer, db *gorm.DB, levelDB *store.LevelDB, licenseService *service.LicenseService, mjPool *mj.ServicePool, sdPool *sd.ServicePool) *ConfigHandler {
|
||||||
return &ConfigHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, levelDB: levelDB, licenseService: licenseService}
|
return &ConfigHandler{
|
||||||
|
BaseHandler: handler.BaseHandler{App: app, DB: db},
|
||||||
|
levelDB: levelDB,
|
||||||
|
mjServicePool: mjPool,
|
||||||
|
sdServicePool: sdPool,
|
||||||
|
licenseService: licenseService,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ConfigHandler) Update(c *gin.Context) {
|
func (h *ConfigHandler) Update(c *gin.Context) {
|
||||||
@ -138,3 +148,49 @@ func (h *ConfigHandler) GetDrawingConfig(c *gin.Context) {
|
|||||||
"sd": h.App.Config.SdConfigs,
|
"sd": h.App.Config.SdConfigs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveDrawingConfig 保存AI绘画配置
|
||||||
|
func (h *ConfigHandler) SaveDrawingConfig(c *gin.Context) {
|
||||||
|
var data struct {
|
||||||
|
Sd []types.StableDiffusionConfig `json:"sd"`
|
||||||
|
MjPlus []types.MjPlusConfig `json:"mj_plus"`
|
||||||
|
MjProxy []types.MjProxyConfig `json:"mj_proxy"`
|
||||||
|
}
|
||||||
|
if err := c.ShouldBindJSON(&data); err != nil {
|
||||||
|
resp.ERROR(c, types.InvalidArgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
changed := false
|
||||||
|
if configChanged(data.Sd, h.App.Config.SdConfigs) {
|
||||||
|
logger.Debugf("SD 配置变动了")
|
||||||
|
h.App.Config.SdConfigs = data.Sd
|
||||||
|
h.sdServicePool.InitServices(data.Sd)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if configChanged(data.MjPlus, h.App.Config.MjPlusConfigs) || configChanged(data.MjProxy, h.App.Config.MjProxyConfigs) {
|
||||||
|
logger.Debugf("MidJourney 配置变动了")
|
||||||
|
h.App.Config.MjPlusConfigs = data.MjPlus
|
||||||
|
h.App.Config.MjProxyConfigs = data.MjProxy
|
||||||
|
h.mjServicePool.InitServices(data.MjPlus, data.MjProxy)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
err := core.SaveConfig(h.App.Config)
|
||||||
|
if err != nil {
|
||||||
|
resp.ERROR(c, "更新配置文档失败!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.SUCCESS(c)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func configChanged(c1 interface{}, c2 interface{}) bool {
|
||||||
|
encode1 := utils.JsonEncode(c1)
|
||||||
|
encode2 := utils.JsonEncode(c2)
|
||||||
|
return utils.Md5(encode1) != utils.Md5(encode2)
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ package admin
|
|||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"geekai/core"
|
"geekai/core"
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/handler"
|
"geekai/handler"
|
||||||
@ -16,7 +17,6 @@ import (
|
|||||||
"geekai/store/vo"
|
"geekai/store/vo"
|
||||||
"geekai/utils"
|
"geekai/utils"
|
||||||
"geekai/utils/resp"
|
"geekai/utils/resp"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -87,7 +87,7 @@ func (h *UserHandler) Save(c *gin.Context) {
|
|||||||
// 检测最大注册人数
|
// 检测最大注册人数
|
||||||
var totalUser int64
|
var totalUser int64
|
||||||
h.DB.Model(&model.User{}).Count(&totalUser)
|
h.DB.Model(&model.User{}).Count(&totalUser)
|
||||||
if int(totalUser) >= h.licenseService.GetLicense().UserNum {
|
if h.licenseService.GetLicense().UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().UserNum {
|
||||||
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
|
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ package handler
|
|||||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"geekai/core"
|
"geekai/core"
|
||||||
"geekai/core/types"
|
"geekai/core/types"
|
||||||
"geekai/service"
|
"geekai/service"
|
||||||
@ -15,7 +16,6 @@ import (
|
|||||||
"geekai/store/vo"
|
"geekai/store/vo"
|
||||||
"geekai/utils"
|
"geekai/utils"
|
||||||
"geekai/utils/resp"
|
"geekai/utils/resp"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ func (h *UserHandler) Register(c *gin.Context) {
|
|||||||
// 检测最大注册人数
|
// 检测最大注册人数
|
||||||
var totalUser int64
|
var totalUser int64
|
||||||
h.DB.Model(&model.User{}).Count(&totalUser)
|
h.DB.Model(&model.User{}).Count(&totalUser)
|
||||||
if int(totalUser) >= h.licenseService.GetLicense().UserNum {
|
if h.licenseService.GetLicense().UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().UserNum {
|
||||||
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
|
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,8 @@ func main() {
|
|||||||
|
|
||||||
// MidJourney service pool
|
// MidJourney service pool
|
||||||
fx.Provide(mj.NewServicePool),
|
fx.Provide(mj.NewServicePool),
|
||||||
fx.Invoke(func(pool *mj.ServicePool) {
|
fx.Invoke(func(pool *mj.ServicePool, config *types.AppConfig) {
|
||||||
|
pool.InitServices(config.MjPlusConfigs, config.MjProxyConfigs)
|
||||||
if pool.HasAvailableService() {
|
if pool.HasAvailableService() {
|
||||||
pool.DownloadImages()
|
pool.DownloadImages()
|
||||||
pool.CheckTaskNotify()
|
pool.CheckTaskNotify()
|
||||||
@ -200,7 +201,8 @@ func main() {
|
|||||||
|
|
||||||
// Stable Diffusion 机器人
|
// Stable Diffusion 机器人
|
||||||
fx.Provide(sd.NewServicePool),
|
fx.Provide(sd.NewServicePool),
|
||||||
fx.Invoke(func(pool *sd.ServicePool) {
|
fx.Invoke(func(pool *sd.ServicePool, config *types.AppConfig) {
|
||||||
|
pool.InitServices(config.SdConfigs)
|
||||||
if pool.HasAvailableService() {
|
if pool.HasAvailableService() {
|
||||||
pool.CheckTaskNotify()
|
pool.CheckTaskNotify()
|
||||||
pool.CheckTaskStatus()
|
pool.CheckTaskStatus()
|
||||||
@ -303,6 +305,7 @@ func main() {
|
|||||||
group.POST("active", h.Active)
|
group.POST("active", h.Active)
|
||||||
group.GET("config/get/license", h.GetLicense)
|
group.GET("config/get/license", h.GetLicense)
|
||||||
group.GET("config/get/draw", h.GetDrawingConfig)
|
group.GET("config/get/draw", h.GetDrawingConfig)
|
||||||
|
group.POST("config/update/draw", h.SaveDrawingConfig)
|
||||||
}),
|
}),
|
||||||
fx.Invoke(func(s *core.AppServer, h *admin.ManagerHandler) {
|
fx.Invoke(func(s *core.AppServer, h *admin.ManagerHandler) {
|
||||||
group := s.Engine.Group("/api/admin/")
|
group := s.Engine.Group("/api/admin/")
|
||||||
|
@ -31,48 +31,15 @@ type ServicePool struct {
|
|||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
uploaderManager *oss.UploaderManager
|
uploaderManager *oss.UploaderManager
|
||||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||||
|
licenseService *service.LicenseService
|
||||||
}
|
}
|
||||||
|
|
||||||
var logger = logger2.GetLogger()
|
var logger = logger2.GetLogger()
|
||||||
|
|
||||||
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig, licenseService *service.LicenseService) *ServicePool {
|
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, licenseService *service.LicenseService) *ServicePool {
|
||||||
services := make([]*Service, 0)
|
services := make([]*Service, 0)
|
||||||
taskQueue := store.NewRedisQueue("MidJourney_Task_Queue", redisCli)
|
taskQueue := store.NewRedisQueue("MidJourney_Task_Queue", redisCli)
|
||||||
notifyQueue := store.NewRedisQueue("MidJourney_Notify_Queue", redisCli)
|
notifyQueue := store.NewRedisQueue("MidJourney_Notify_Queue", redisCli)
|
||||||
|
|
||||||
for k, config := range appConfig.MjPlusConfigs {
|
|
||||||
if config.Enabled == false {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err := licenseService.IsValidApiURL(config.ApiURL)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cli := NewPlusClient(config)
|
|
||||||
name := fmt.Sprintf("mj-plus-service-%d", k)
|
|
||||||
plusService := NewService(name, taskQueue, notifyQueue, db, cli)
|
|
||||||
go func() {
|
|
||||||
plusService.Run()
|
|
||||||
}()
|
|
||||||
services = append(services, plusService)
|
|
||||||
}
|
|
||||||
|
|
||||||
// for mid-journey proxy
|
|
||||||
for k, config := range appConfig.MjProxyConfigs {
|
|
||||||
if config.Enabled == false {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cli := NewProxyClient(config)
|
|
||||||
name := fmt.Sprintf("mj-proxy-service-%d", k)
|
|
||||||
proxyService := NewService(name, taskQueue, notifyQueue, db, cli)
|
|
||||||
go func() {
|
|
||||||
proxyService.Run()
|
|
||||||
}()
|
|
||||||
services = append(services, proxyService)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ServicePool{
|
return &ServicePool{
|
||||||
taskQueue: taskQueue,
|
taskQueue: taskQueue,
|
||||||
notifyQueue: notifyQueue,
|
notifyQueue: notifyQueue,
|
||||||
@ -80,6 +47,47 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
|||||||
uploaderManager: manager,
|
uploaderManager: manager,
|
||||||
db: db,
|
db: db,
|
||||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||||
|
licenseService: licenseService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ServicePool) InitServices(plusConfigs []types.MjPlusConfig, proxyConfigs []types.MjProxyConfig) {
|
||||||
|
// stop old service
|
||||||
|
for _, s := range p.services {
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, config := range plusConfigs {
|
||||||
|
if config.Enabled == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := p.licenseService.IsValidApiURL(config.ApiURL)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("创建 MJ-PLUS 服务失败:%v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := NewPlusClient(config)
|
||||||
|
name := fmt.Sprintf("mj-plus-service-%d", k)
|
||||||
|
plusService := NewService(name, p.taskQueue, p.notifyQueue, p.db, cli)
|
||||||
|
go func() {
|
||||||
|
plusService.Run()
|
||||||
|
}()
|
||||||
|
p.services = append(p.services, plusService)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for mid-journey proxy
|
||||||
|
for k, config := range proxyConfigs {
|
||||||
|
if config.Enabled == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cli := NewProxyClient(config)
|
||||||
|
name := fmt.Sprintf("mj-proxy-service-%d", k)
|
||||||
|
proxyService := NewService(name, p.taskQueue, p.notifyQueue, p.db, cli)
|
||||||
|
go func() {
|
||||||
|
proxyService.Run()
|
||||||
|
}()
|
||||||
|
p.services = append(p.services, proxyService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ type Service struct {
|
|||||||
taskQueue *store.RedisQueue
|
taskQueue *store.RedisQueue
|
||||||
notifyQueue *store.RedisQueue
|
notifyQueue *store.RedisQueue
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
|
running bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(name string, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, db *gorm.DB, cli Client) *Service {
|
func NewService(name string, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, db *gorm.DB, cli Client) *Service {
|
||||||
@ -37,12 +38,13 @@ func NewService(name string, taskQueue *store.RedisQueue, notifyQueue *store.Red
|
|||||||
taskQueue: taskQueue,
|
taskQueue: taskQueue,
|
||||||
notifyQueue: notifyQueue,
|
notifyQueue: notifyQueue,
|
||||||
Client: cli,
|
Client: cli,
|
||||||
|
running: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Run() {
|
func (s *Service) Run() {
|
||||||
logger.Infof("Starting MidJourney job consumer for %s", s.Name)
|
logger.Infof("Starting MidJourney job consumer for %s", s.Name)
|
||||||
for {
|
for s.running {
|
||||||
var task types.MjTask
|
var task types.MjTask
|
||||||
err := s.taskQueue.LPop(&task)
|
err := s.taskQueue.LPop(&task)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -125,6 +127,11 @@ func (s *Service) Run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) Stop() {
|
||||||
|
s.running = false
|
||||||
|
s.Client = nil
|
||||||
|
}
|
||||||
|
|
||||||
type CBReq struct {
|
type CBReq struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
|
@ -25,28 +25,14 @@ type ServicePool struct {
|
|||||||
notifyQueue *store.RedisQueue
|
notifyQueue *store.RedisQueue
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||||
|
uploader *oss.UploaderManager
|
||||||
|
levelDB *store.LevelDB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig, levelDB *store.LevelDB) *ServicePool {
|
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, levelDB *store.LevelDB) *ServicePool {
|
||||||
services := make([]*Service, 0)
|
services := make([]*Service, 0)
|
||||||
taskQueue := store.NewRedisQueue("StableDiffusion_Task_Queue", redisCli)
|
taskQueue := store.NewRedisQueue("StableDiffusion_Task_Queue", redisCli)
|
||||||
notifyQueue := store.NewRedisQueue("StableDiffusion_Queue", redisCli)
|
notifyQueue := store.NewRedisQueue("StableDiffusion_Queue", redisCli)
|
||||||
// create mj client and service
|
|
||||||
for _, config := range appConfig.SdConfigs {
|
|
||||||
if config.Enabled == false {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// create sd service
|
|
||||||
name := fmt.Sprintf("StableDifffusion Service-%s", config.Model)
|
|
||||||
service := NewService(name, config, taskQueue, notifyQueue, db, manager, levelDB)
|
|
||||||
// run sd service
|
|
||||||
go func() {
|
|
||||||
service.Run()
|
|
||||||
}()
|
|
||||||
|
|
||||||
services = append(services, service)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ServicePool{
|
return &ServicePool{
|
||||||
taskQueue: taskQueue,
|
taskQueue: taskQueue,
|
||||||
@ -54,6 +40,31 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
|||||||
services: services,
|
services: services,
|
||||||
db: db,
|
db: db,
|
||||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||||
|
uploader: manager,
|
||||||
|
levelDB: levelDB,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ServicePool) InitServices(configs []types.StableDiffusionConfig) {
|
||||||
|
// stop old service
|
||||||
|
for _, s := range p.services {
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, config := range configs {
|
||||||
|
if config.Enabled == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// create sd service
|
||||||
|
name := fmt.Sprintf(" sd-service-%d", k)
|
||||||
|
service := NewService(name, config, p.taskQueue, p.notifyQueue, p.db, p.uploader, p.levelDB)
|
||||||
|
// run sd service
|
||||||
|
go func() {
|
||||||
|
service.Run()
|
||||||
|
}()
|
||||||
|
|
||||||
|
p.services = append(p.services, service)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ type Service struct {
|
|||||||
uploadManager *oss.UploaderManager
|
uploadManager *oss.UploaderManager
|
||||||
name string // service name
|
name string // service name
|
||||||
leveldb *store.LevelDB
|
leveldb *store.LevelDB
|
||||||
|
running bool // 运行状态
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(name string, config types.StableDiffusionConfig, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, db *gorm.DB, manager *oss.UploaderManager, levelDB *store.LevelDB) *Service {
|
func NewService(name string, config types.StableDiffusionConfig, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, db *gorm.DB, manager *oss.UploaderManager, levelDB *store.LevelDB) *Service {
|
||||||
@ -46,18 +47,20 @@ func NewService(name string, config types.StableDiffusionConfig, taskQueue *stor
|
|||||||
db: db,
|
db: db,
|
||||||
leveldb: levelDB,
|
leveldb: levelDB,
|
||||||
uploadManager: manager,
|
uploadManager: manager,
|
||||||
|
running: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Run() {
|
func (s *Service) Run() {
|
||||||
for {
|
logger.Infof("Starting Stable-Diffusion job consumer for %s", s.name)
|
||||||
|
for s.running {
|
||||||
var task types.SdTask
|
var task types.SdTask
|
||||||
err := s.taskQueue.LPop(&task)
|
err := s.taskQueue.LPop(&task)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("taking task with error: %v", err)
|
logger.Errorf("taking task with error: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
logger.Infof("%s handle a new Stable-Diffusion task: %+v", s.name, task)
|
||||||
// translate prompt
|
// translate prompt
|
||||||
if utils.HasChinese(task.Params.Prompt) {
|
if utils.HasChinese(task.Params.Prompt) {
|
||||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, task.Params.Prompt))
|
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, task.Params.Prompt))
|
||||||
@ -94,6 +97,10 @@ func (s *Service) Run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) Stop() {
|
||||||
|
s.running = false
|
||||||
|
}
|
||||||
|
|
||||||
// Txt2ImgReq 文生图请求实体
|
// Txt2ImgReq 文生图请求实体
|
||||||
type Txt2ImgReq struct {
|
type Txt2ImgReq struct {
|
||||||
Prompt string `json:"prompt"`
|
Prompt string `json:"prompt"`
|
||||||
@ -104,6 +111,7 @@ type Txt2ImgReq struct {
|
|||||||
Width int `json:"width"`
|
Width int `json:"width"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
SamplerName string `json:"sampler_name"`
|
SamplerName string `json:"sampler_name"`
|
||||||
|
Scheduler string `json:"scheduler"`
|
||||||
EnableHr bool `json:"enable_hr,omitempty"`
|
EnableHr bool `json:"enable_hr,omitempty"`
|
||||||
HrScale int `json:"hr_scale,omitempty"`
|
HrScale int `json:"hr_scale,omitempty"`
|
||||||
HrUpscaler string `json:"hr_upscaler,omitempty"`
|
HrUpscaler string `json:"hr_upscaler,omitempty"`
|
||||||
@ -137,6 +145,7 @@ func (s *Service) Txt2Img(task types.SdTask) error {
|
|||||||
Width: task.Params.Width,
|
Width: task.Params.Width,
|
||||||
Height: task.Params.Height,
|
Height: task.Params.Height,
|
||||||
SamplerName: task.Params.Sampler,
|
SamplerName: task.Params.Sampler,
|
||||||
|
Scheduler: task.Params.Scheduler,
|
||||||
ForceTaskId: task.Params.TaskId,
|
ForceTaskId: task.Params.TaskId,
|
||||||
}
|
}
|
||||||
if task.Params.Seed > 0 {
|
if task.Params.Seed > 0 {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
||||||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
||||||
<title>ChatGPT-Plus</title>
|
<title>Geek-AI 创作助手</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -105,7 +105,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/admin/login',
|
path: '/admin/login',
|
||||||
name: 'admin-login',
|
name: 'admin-login',
|
||||||
meta: {title: 'ChatPuls 控制台登录'},
|
meta: {title: 'Geek-AI 控制台登录'},
|
||||||
component: () => import('@/views/admin/Login.vue'),
|
component: () => import('@/views/admin/Login.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -113,7 +113,7 @@ const routes = [
|
|||||||
path: '/admin',
|
path: '/admin',
|
||||||
redirect: '/admin/dashboard',
|
redirect: '/admin/dashboard',
|
||||||
component: () => import("@/views/admin/Home.vue"),
|
component: () => import("@/views/admin/Home.vue"),
|
||||||
meta: {title: 'ChatPuls 管理后台'},
|
meta: {title: 'Geek-AI 控制台'},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/admin/dashboard',
|
path: '/admin/dashboard',
|
||||||
|
@ -29,6 +29,28 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="param-line" style="padding-top: 10px">
|
||||||
|
<el-form-item label="采样调度器">
|
||||||
|
<template #default>
|
||||||
|
<div class="form-item-inner">
|
||||||
|
<el-select v-model="params.scheduler" style="width:176px">
|
||||||
|
<el-option v-for="item in schedulers" :label="item" :value="item" :key="item"/>
|
||||||
|
</el-select>
|
||||||
|
<el-tooltip
|
||||||
|
effect="light"
|
||||||
|
content="推荐自动或者 Karras"
|
||||||
|
raw-content
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<el-icon class="info-icon">
|
||||||
|
<InfoFilled/>
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="param-line">
|
<div class="param-line">
|
||||||
<el-form-item label="图片尺寸">
|
<el-form-item label="图片尺寸">
|
||||||
<template #default>
|
<template #default>
|
||||||
@ -509,12 +531,14 @@ window.onresize = () => {
|
|||||||
listBoxHeight.value = window.innerHeight - 40
|
listBoxHeight.value = window.innerHeight - 40
|
||||||
paramBoxHeight.value = window.innerHeight - 150
|
paramBoxHeight.value = window.innerHeight - 150
|
||||||
}
|
}
|
||||||
const samplers = ["Euler a", "DPM++ 2S a Karras", "DPM++ 2M Karras", "DPM++ SDE Karras", "DPM++ 2M SDE Karras"]
|
const samplers = ["Euler a", "DPM++ 2S a", "DPM++ 2M", "DPM++ SDE", "DPM++ 2M SDE", "UniPC", "Restart"]
|
||||||
|
const schedulers = ["Automatic", "Karras", "Exponential", "Uniform"]
|
||||||
const scaleAlg = ["Latent", "ESRGAN_4x", "R-ESRGAN 4x+", "SwinIR_4x", "LDSR"]
|
const scaleAlg = ["Latent", "ESRGAN_4x", "R-ESRGAN 4x+", "SwinIR_4x", "LDSR"]
|
||||||
const params = ref({
|
const params = ref({
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 1024,
|
height: 1024,
|
||||||
sampler: samplers[0],
|
sampler: samplers[0],
|
||||||
|
scheduler: schedulers[0],
|
||||||
seed: -1,
|
seed: -1,
|
||||||
steps: 20,
|
steps: 20,
|
||||||
cfg_scale: 7,
|
cfg_scale: 7,
|
||||||
|
@ -1,15 +1,34 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form label-width="150px" label-position="right">
|
<el-form label-width="150px" label-position="right" class="draw-config">
|
||||||
<el-tabs type="border-card">
|
<el-tabs type="border-card">
|
||||||
<el-tab-pane label="MJ-PLUS">
|
<el-tab-pane label="MJ-PLUS">
|
||||||
<div v-if="mjPlusConfigs">
|
<div v-if="mjPlusConfigs">
|
||||||
<el-form-item label="网站标题">
|
<div class="config-item" v-for="(v,k) in mjPlusConfigs">
|
||||||
<el-input v-model="sdConfigs"/>
|
<el-form-item label="是否启用">
|
||||||
</el-form-item>
|
<el-switch v-model="v['Enabled']"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API 地址">
|
||||||
|
<el-input v-model="v['ApiURL']" placeholder="API 地址"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API 令牌">
|
||||||
|
<el-input v-model="v['ApiKey']" placeholder="API KEY"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="绘画模式">
|
||||||
|
<el-select v-model="v['Mode']" placeholder="请选择模式">
|
||||||
|
<el-option v-for="item in mjModels" :value="item.value" :label="item.name" :key="item.value">{{
|
||||||
|
item.name
|
||||||
|
}}
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-button class="remove" type="danger" :icon="Delete" circle @click="removeItem(mjPlusConfigs,k)"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-else></el-empty>
|
<el-empty v-else></el-empty>
|
||||||
<el-row style="justify-content: center">
|
|
||||||
<el-button round>
|
<el-row style="justify-content: center; padding: 10px">
|
||||||
|
<el-button round @click="addConfig(mjPlusConfigs)">
|
||||||
<el-icon><Plus /></el-icon>
|
<el-icon><Plus /></el-icon>
|
||||||
<span>新增配置</span>
|
<span>新增配置</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -19,23 +38,66 @@
|
|||||||
|
|
||||||
<el-tab-pane label="MJ-PROXY">
|
<el-tab-pane label="MJ-PROXY">
|
||||||
<div v-if="mjProxyConfigs">
|
<div v-if="mjProxyConfigs">
|
||||||
<el-form-item label="注册赠送算力">
|
<div class="config-item" v-for="(v,k) in mjProxyConfigs">
|
||||||
<el-input v-model.number="sdConfigs" placeholder="新用户注册赠送算力"/>
|
<el-form-item label="是否启用">
|
||||||
</el-form-item>
|
<el-switch v-model="v['Enabled']"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API 地址">
|
||||||
|
<el-input v-model="v['ApiURL']" placeholder="API 地址"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API 令牌">
|
||||||
|
<el-input v-model="v['ApiKey']" placeholder="API KEY"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-button class="remove" type="danger" :icon="Delete" circle @click="removeItem(mjProxyConfigs,k)"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-else />
|
<el-empty v-else />
|
||||||
|
|
||||||
|
<el-row style="justify-content: center; padding: 10px">
|
||||||
|
<el-button round @click="addConfig(mjProxyConfigs)">
|
||||||
|
<el-icon>
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
<span>新增配置</span>
|
||||||
|
</el-button>
|
||||||
|
</el-row>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="Stable-Diffusion">
|
<el-tab-pane label="Stable-Diffusion">
|
||||||
<el-form-item label="注册赠送算力">
|
<div v-if="sdConfigs">
|
||||||
<el-input v-model.number="sdConfigs" placeholder="新用户注册赠送算力"/>
|
<div class="config-item" v-for="(v,k) in sdConfigs">
|
||||||
</el-form-item>
|
<el-form-item label="是否启用">
|
||||||
|
<el-switch v-model="v['Enabled']"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API 地址">
|
||||||
|
<el-input v-model="v['ApiURL']" placeholder="API 地址"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="API 令牌">
|
||||||
|
<el-input v-model="v['ApiKey']" placeholder="API KEY"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="模型">
|
||||||
|
<el-input v-model="v['Model']" placeholder="绘画模型"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-button class="remove" type="danger" :icon="Delete" circle @click="removeItem(sdConfigs,k)"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else/>
|
||||||
|
|
||||||
|
<el-row style="justify-content: center; padding: 10px">
|
||||||
|
<el-button round @click="addConfig(sdConfigs)">
|
||||||
|
<el-icon>
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
<span>新增配置</span>
|
||||||
|
</el-button>
|
||||||
|
</el-row>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
||||||
<div style="padding: 10px;">
|
<div style="padding: 10px;">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="save('system')">保存</el-button>
|
<el-button type="primary" @click="saveConfig()">保存</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -43,14 +105,19 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {httpGet} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
import {Plus} from "@element-plus/icons-vue";
|
import {Delete, Plus} from "@element-plus/icons-vue";
|
||||||
|
|
||||||
// 变量定义
|
// 变量定义
|
||||||
const sdConfigs = ref([])
|
const sdConfigs = ref([])
|
||||||
const mjPlusConfigs = ref([])
|
const mjPlusConfigs = ref([])
|
||||||
const mjProxyConfigs = ref([])
|
const mjProxyConfigs = ref([])
|
||||||
|
const mjModels = ref([
|
||||||
|
{name: "慢速(Relax)", value: "relax"},
|
||||||
|
{name: "快速(Fast)", value: "fast"},
|
||||||
|
{name: "急速(Turbo)", value: "turbo"},
|
||||||
|
])
|
||||||
|
|
||||||
httpGet("/api/admin/config/get/draw").then(res => {
|
httpGet("/api/admin/config/get/draw").then(res => {
|
||||||
sdConfigs.value = res.data.sd
|
sdConfigs.value = res.data.sd
|
||||||
@ -59,29 +126,48 @@ httpGet("/api/admin/config/get/draw").then(res => {
|
|||||||
}).catch(e =>{
|
}).catch(e =>{
|
||||||
ElMessage.error("获取AI绘画配置失败:"+e.message)
|
ElMessage.error("获取AI绘画配置失败:"+e.message)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const addConfig = (configs) => {
|
||||||
|
configs.push({
|
||||||
|
Enabled: true,
|
||||||
|
ApiKey: '',
|
||||||
|
ApiURL: '',
|
||||||
|
Mode: 'fast'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveConfig = () => {
|
||||||
|
httpPost('/api/admin/config/update/draw', {
|
||||||
|
'sd': sdConfigs.value,
|
||||||
|
'mj_plus': mjPlusConfigs.value,
|
||||||
|
'mj_proxy': mjProxyConfigs.value
|
||||||
|
}).then(() => {
|
||||||
|
ElMessage.success("配置更新成功")
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error("操作失败:" + e.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeItem = (arr, k) => {
|
||||||
|
arr.splice(k, 1)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.menu {
|
.draw-config {
|
||||||
|
|
||||||
.opt-box {
|
.config-item {
|
||||||
padding-bottom: 10px;
|
position relative
|
||||||
display flex;
|
padding 15px 10px 10px 10px
|
||||||
justify-content flex-end
|
border 1px solid var(--el-border-color)
|
||||||
|
border-radius 10px
|
||||||
|
margin-bottom 10px
|
||||||
|
|
||||||
.el-icon {
|
.remove {
|
||||||
margin-right: 5px;
|
position absolute
|
||||||
|
right 15px
|
||||||
|
top 15px
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-icon {
|
|
||||||
width 36px
|
|
||||||
height 36px
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-select {
|
|
||||||
width: 100%
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -2,6 +2,10 @@
|
|||||||
<div class="admin-login">
|
<div class="admin-login">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="contain">
|
<div class="contain">
|
||||||
|
<div class="logo">
|
||||||
|
<el-image :src="logo" fit="cover" @click="router.push('/')"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="header">{{ title }}</div>
|
<div class="header">{{ title }}</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="block">
|
<div class="block">
|
||||||
@ -42,9 +46,9 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
import {onMounted, ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {Lock, UserFilled} from "@element-plus/icons-vue";
|
import {Lock, UserFilled} from "@element-plus/icons-vue";
|
||||||
import {httpPost} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import FooterBar from "@/components/FooterBar.vue";
|
import FooterBar from "@/components/FooterBar.vue";
|
||||||
@ -52,15 +56,24 @@ import {setAdminToken} from "@/store/session";
|
|||||||
import {checkAdminSession} from "@/action/session";
|
import {checkAdminSession} from "@/action/session";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const title = ref('ChatGPT Plus Admin');
|
const title = ref('Geek-AI Console');
|
||||||
const username = ref(process.env.VUE_APP_ADMIN_USER);
|
const username = ref(process.env.VUE_APP_ADMIN_USER);
|
||||||
const password = ref(process.env.VUE_APP_ADMIN_PASS);
|
const password = ref(process.env.VUE_APP_ADMIN_PASS);
|
||||||
|
const logo = ref("/images/logo.png")
|
||||||
|
|
||||||
checkAdminSession().then(() => {
|
checkAdminSession().then(() => {
|
||||||
router.push("/admin")
|
router.push("/admin")
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 加载系统配置
|
||||||
|
httpGet('/api/config/get?key=system').then(res => {
|
||||||
|
title.value = res.data.admin_title
|
||||||
|
logo.value = res.data.logo
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error("加载系统配置失败: " + e.message)
|
||||||
|
})
|
||||||
|
|
||||||
const keyupHandle = (e) => {
|
const keyupHandle = (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
login();
|
login();
|
||||||
@ -107,10 +120,20 @@ const login = function () {
|
|||||||
border-radius 10px;
|
border-radius 10px;
|
||||||
background rgba(255, 255, 255, 0.3)
|
background rgba(255, 255, 255, 0.3)
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
text-align center
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
width 120px;
|
||||||
|
cursor pointer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
width 100%
|
width 100%
|
||||||
margin-bottom 20px
|
margin-bottom 20px
|
||||||
font-size 24px
|
padding 10px
|
||||||
|
font-size 26px
|
||||||
text-align center
|
text-align center
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +279,9 @@
|
|||||||
<el-tab-pane label="公告配置" name="notice">
|
<el-tab-pane label="公告配置" name="notice">
|
||||||
<md-editor class="mgb20" v-model="notice" @on-upload-img="onUploadImg"/>
|
<md-editor class="mgb20" v-model="notice" @on-upload-img="onUploadImg"/>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="save('notice')">保存</el-button>
|
<div style="padding-top: 10px;margin-left: 150px;">
|
||||||
|
<el-button type="primary" @click="save('notice')">保存</el-button>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user