diff --git a/api/core/types/web.go b/api/core/types/web.go index 43ed032c..9d0413c5 100644 --- a/api/core/types/web.go +++ b/api/core/types/web.go @@ -34,4 +34,5 @@ const ( OkMsg = "Success" ErrorMsg = "系统开小差了" InvalidArgs = "非法参数或参数解析失败" + NoData = "No Data" ) diff --git a/api/handler/mj_handler.go b/api/handler/mj_handler.go index 7d668cc4..13ed5daf 100644 --- a/api/handler/mj_handler.go +++ b/api/handler/mj_handler.go @@ -6,6 +6,7 @@ import ( "chatplus/service" "chatplus/service/oss" "chatplus/store/model" + "chatplus/store/vo" "chatplus/utils" "chatplus/utils/resp" "encoding/base64" @@ -23,6 +24,7 @@ type TaskStatus string const ( Stopped = TaskStatus("Stopped") Finished = TaskStatus("Finished") + Running = TaskStatus("Running") ) type Image struct { @@ -190,6 +192,7 @@ func (h *MidJourneyHandler) notifyHandler(c *gin.Context, data notifyData) (erro // save the job job.UserId = task.UserId + job.Type = task.Type.String() job.MessageId = data.MessageId job.ReferenceId = data.ReferenceId job.Prompt = data.Prompt @@ -242,6 +245,7 @@ func (h *MidJourneyHandler) Proxy(c *gin.Context) { } type reqVo struct { + Src string `json:"src"` Index int32 `json:"index"` MessageId string `json:"message_id"` MessageHash string `json:"message_hash"` @@ -259,15 +263,11 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } - wsClient := h.App.ChatClients.Get(data.SessionId) - if wsClient == nil { - resp.ERROR(c, "No Websocket client online") - return - } + userId, _ := c.Get(types.LoginUserID) h.mjService.PushTask(service.MjTask{ Id: data.SessionId, - Src: service.TaskSrcChat, + Src: service.TaskSrc(data.Src), Type: service.Upscale, Prompt: data.Prompt, UserId: utils.IntValue(utils.InterfaceToString(userId), 0), @@ -279,30 +279,29 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) { MessageHash: data.MessageHash, }) - content := fmt.Sprintf("**%s** 已推送 upscale 任务到 MidJourney 机器人,请耐心等待任务执行...", data.Prompt) - utils.ReplyMessage(wsClient, content) - if h.App.MjTaskClients.Get(data.SessionId) == nil { - h.App.MjTaskClients.Put(data.SessionId, wsClient) + wsClient := h.App.ChatClients.Get(data.SessionId) + if wsClient != nil { + content := fmt.Sprintf("**%s** 已推送 upscale 任务到 MidJourney 机器人,请耐心等待任务执行...", data.Prompt) + utils.ReplyMessage(wsClient, content) + if h.App.MjTaskClients.Get(data.SessionId) == nil { + h.App.MjTaskClients.Put(data.SessionId, wsClient) + } } resp.SUCCESS(c) } +// Variation send variation command to MidJourney Bot func (h *MidJourneyHandler) Variation(c *gin.Context) { var data reqVo if err := c.ShouldBindJSON(&data); err != nil || data.SessionId == "" { resp.ERROR(c, types.InvalidArgs) return } - wsClient := h.App.ChatClients.Get(data.SessionId) - if wsClient == nil { - resp.ERROR(c, "No Websocket client online") - return - } userId, _ := c.Get(types.LoginUserID) h.mjService.PushTask(service.MjTask{ Id: data.SessionId, - Src: service.TaskSrcChat, + Src: service.TaskSrc(data.Src), Type: service.Variation, Prompt: data.Prompt, UserId: utils.IntValue(utils.InterfaceToString(userId), 0), @@ -313,10 +312,43 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) { MessageId: data.MessageId, MessageHash: data.MessageHash, }) - content := fmt.Sprintf("**%s** 已推送 variation 任务到 MidJourney 机器人,请耐心等待任务执行...", data.Prompt) - utils.ReplyMessage(wsClient, content) - if h.App.MjTaskClients.Get(data.SessionId) == nil { - h.App.MjTaskClients.Put(data.SessionId, wsClient) + + // 从聊天窗口发送的请求,记录客户端信息 + wsClient := h.App.ChatClients.Get(data.SessionId) + if wsClient != nil { + content := fmt.Sprintf("**%s** 已推送 variation 任务到 MidJourney 机器人,请耐心等待任务执行...", data.Prompt) + utils.ReplyMessage(wsClient, content) + if h.App.MjTaskClients.Get(data.SessionId) == nil { + h.App.MjTaskClients.Put(data.SessionId, wsClient) + } } resp.SUCCESS(c) } + +// JobList 获取 MJ 任务列表 +func (h *MidJourneyHandler) JobList(c *gin.Context) { + status := h.GetInt(c, "status", 0) + var items []model.MidJourneyJob + var res *gorm.DB + userId, _ := c.Get(types.LoginUserID) + if status == 1 { + res = h.db.Where("user_id = ? AND progress = 100", userId).Find(&items) + } else { + res = h.db.Where("user_id = ? AND progress < 100", userId).Find(&items) + } + if res.Error != nil { + resp.ERROR(c, types.NoData) + return + } + + var jobs = make([]vo.MidJourneyJob, 0) + for _, item := range items { + var job vo.MidJourneyJob + err := utils.CopyObject(item, &job) + if err != nil { + continue + } + jobs = append(jobs, job) + } + resp.SUCCESS(c, jobs) +} diff --git a/api/main.go b/api/main.go index 4d6738ec..5bbdb951 100644 --- a/api/main.go +++ b/api/main.go @@ -193,6 +193,7 @@ func main() { group.POST("notify", h.Notify) group.POST("upscale", h.Upscale) group.POST("variation", h.Variation) + group.GET("jobs", h.JobList) group.GET("proxy", h.Proxy) }), diff --git a/api/service/mj_service.go b/api/service/mj_service.go index c26adce1..1f583771 100644 --- a/api/service/mj_service.go +++ b/api/service/mj_service.go @@ -21,6 +21,10 @@ const MjRunningJobKey = "MidJourney_Running_Job" type TaskType string +func (t TaskType) String() string { + return string(t) +} + const ( Image = TaskType("image") Upscale = TaskType("upscale") @@ -100,9 +104,9 @@ func (s *MjService) Run() { } if err != nil { logger.Error("绘画任务执行失败:", err) - if task.RetryCount > 5 { - continue - } + //if task.RetryCount > 5 { + // continue + //} task.RetryCount += 1 s.taskQueue.RPush(task) // TODO: 执行失败通知聊天客户端 diff --git a/api/store/model/mj_job.go b/api/store/model/mj_job.go index 30ac00ca..39848fb7 100644 --- a/api/store/model/mj_job.go +++ b/api/store/model/mj_job.go @@ -4,6 +4,7 @@ import "time" type MidJourneyJob struct { Id uint `gorm:"primarykey;column:id"` + Type string UserId int MessageId string ReferenceId string diff --git a/api/store/vo/mj_job.go b/api/store/vo/mj_job.go new file mode 100644 index 00000000..1cd47cd8 --- /dev/null +++ b/api/store/vo/mj_job.go @@ -0,0 +1,16 @@ +package vo + +import "time" + +type MidJourneyJob struct { + Id uint `json:"id"` + Type string `json:"type"` + UserId int `json:"user_id"` + MessageId string `json:"message_id"` + ReferenceId string `json:"reference_id"` + ImgURL string `json:"img_url"` + Hash string `json:"hash"` + Progress int `json:"progress"` + Prompt string `json:"prompt"` + CreatedAt time.Time `json:"created_at"` +} diff --git a/database/update-v3.1.3.sql b/database/update-v3.1.3.sql index 984020cc..e86a2934 100644 --- a/database/update-v3.1.3.sql +++ b/database/update-v3.1.3.sql @@ -4,4 +4,7 @@ ALTER TABLE `chatgpt_mj_jobs` DROP `content`; ALTER TABLE `chatgpt_mj_jobs` DROP `chat_id`; ALTER TABLE `chatgpt_mj_jobs` ADD `progress` SMALLINT(5) NULL DEFAULT '0' COMMENT '任务进度' AFTER `prompt`; ALTER TABLE `chatgpt_mj_jobs` ADD `hash` VARCHAR(100) NULL DEFAULT NULL COMMENT 'message hash' AFTER `prompt`; -ALTER TABLE `chatgpt_mj_jobs` ADD `img_url` VARCHAR(255) NULL DEFAULT NULL COMMENT '图片URL' AFTER `prompt`; \ No newline at end of file +ALTER TABLE `chatgpt_mj_jobs` ADD `img_url` VARCHAR(255) NULL DEFAULT NULL COMMENT '图片URL' AFTER `prompt`; + +-- 2023-09-15 +ALTER TABLE `chatgpt_mj_jobs` ADD `type` VARCHAR(20) NULL DEFAULT 'image' COMMENT '任务类别' AFTER `user_id`; \ No newline at end of file diff --git a/web/src/assets/css/image-mj.css b/web/src/assets/css/image-mj.css index efdfd297..f8ed684f 100644 --- a/web/src/assets/css/image-mj.css +++ b/web/src/assets/css/image-mj.css @@ -3,6 +3,10 @@ } .page-mj .inner { display: flex; +/* 修改滚动条的颜色 */ +/* 修改滚动条轨道的背景颜色 */ +/* 修改滚动条的滑块颜色 */ +/* 修改滚动条的滑块的悬停颜色 */ } .page-mj .inner .mj-box { margin: 10px; @@ -143,27 +147,60 @@ .page-mj .inner .el-form .el-slider { width: 180px; } +.page-mj .inner ::-webkit-scrollbar { + width: 10px; /* 滚动条宽度 */ +} +.page-mj .inner ::-webkit-scrollbar-track { + background-color: #282c34; +} +.page-mj .inner ::-webkit-scrollbar-thumb { + background-color: #444; + border-radius: 10px; +} +.page-mj .inner ::-webkit-scrollbar-thumb:hover { + background-color: #666; +} .page-mj .inner .task-list-box { width: 100%; padding: 10px; color: #fff; overflow-x: hidden; - overflow-y: auto; } -.page-mj .inner .task-list-box .task-list-inner .grid-content { +.page-mj .inner .task-list-box .running-job-list .job-item { + width: 100%; + padding: 2px; + background-color: #555; +} +.page-mj .inner .task-list-box .running-job-list .job-item .el-image { + width: 100%; + height: 100%; +} +.page-mj .inner .task-list-box .running-job-list .job-item .el-image .image-slot { + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; + height: 100%; + color: #fff; +} +.page-mj .inner .task-list-box .running-job-list .job-item .el-image .image-slot .iconfont { + font-size: 50px; + margin-bottom: 10px; +} +.page-mj .inner .task-list-box .task-list-inner .job-item { margin-bottom: 20px; } -.page-mj .inner .task-list-box .task-list-inner .grid-content .opt .opt-line { +.page-mj .inner .task-list-box .task-list-inner .job-item .opt .opt-line { margin: 6px 0; } -.page-mj .inner .task-list-box .task-list-inner .grid-content .opt .opt-line ul { +.page-mj .inner .task-list-box .task-list-inner .job-item .opt .opt-line ul { display: flex; flex-flow: row; } -.page-mj .inner .task-list-box .task-list-inner .grid-content .opt .opt-line ul li { +.page-mj .inner .task-list-box .task-list-inner .job-item .opt .opt-line ul li { margin-right: 10px; } -.page-mj .inner .task-list-box .task-list-inner .grid-content .opt .opt-line ul li a { +.page-mj .inner .task-list-box .task-list-inner .job-item .opt .opt-line ul li a { padding: 3px 0; width: 50px; text-align: center; @@ -173,6 +210,12 @@ background-color: #4e5058; color: #fff; } -.page-mj .inner .task-list-box .task-list-inner .grid-content .opt .opt-line ul li a:hover { +.page-mj .inner .task-list-box .task-list-inner .job-item .opt .opt-line ul li a:hover { background-color: #6d6f78; } +.mj-list-item-prompt .el-icon { + margin-left: 10px; + cursor: pointer; + position: relative; + top: 2px; +} diff --git a/web/src/assets/css/image-mj.styl b/web/src/assets/css/image-mj.styl index 269b81dd..14469e49 100644 --- a/web/src/assets/css/image-mj.styl +++ b/web/src/assets/css/image-mj.styl @@ -182,17 +182,68 @@ } } + /* 修改滚动条的颜色 */ + + ::-webkit-scrollbar { + width: 10px; /* 滚动条宽度 */ + } + + /* 修改滚动条轨道的背景颜色 */ + + ::-webkit-scrollbar-track { + background-color: #282C34; + } + + /* 修改滚动条的滑块颜色 */ + + ::-webkit-scrollbar-thumb { + background-color: #444444; + border-radius 10px + } + + /* 修改滚动条的滑块的悬停颜色 */ + + ::-webkit-scrollbar-thumb:hover { + background-color: #666666; + } .task-list-box { width 100% padding 10px color #ffffff overflow-x hidden - overflow-y auto + + .running-job-list { + .job-item { + //border: 1px solid #454545; + width: 100%; + padding 2px + background-color #555555 + + .el-image { + width 100% + height 100% + + .image-slot { + display flex + flex-flow column + justify-content center + align-items center + height 100% + color #ffffff + + .iconfont { + font-size 50px + margin-bottom 10px + } + } + } + } + } .task-list-inner { - .grid-content { + .job-item { margin-bottom 20px .opt { @@ -231,4 +282,13 @@ } } +} + +.mj-list-item-prompt { + .el-icon { + margin-left 10px + cursor pointer + position relative + top 2px + } } \ No newline at end of file diff --git a/web/src/components/ChatMidJourney.vue b/web/src/components/ChatMidJourney.vue index fd4fd49c..8f3a5842 100644 --- a/web/src/components/ChatMidJourney.vue +++ b/web/src/components/ChatMidJourney.vue @@ -109,6 +109,7 @@ const send = (url, index) => { emits('disable-input') httpPost(url, { index: index, + src: "chat", message_id: data.value?.["message_id"], message_hash: data.value?.["image"]?.hash, session_id: getSessionId(), diff --git a/web/src/components/ItemList.vue b/web/src/components/ItemList.vue new file mode 100644 index 00000000..757d057a --- /dev/null +++ b/web/src/components/ItemList.vue @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/components/mobile/ChatMidJourney.vue b/web/src/components/mobile/ChatMidJourney.vue index 4b965495..087ef13b 100644 --- a/web/src/components/mobile/ChatMidJourney.vue +++ b/web/src/components/mobile/ChatMidJourney.vue @@ -69,6 +69,8 @@ import {showNotify} from "vant"; const props = defineProps({ content: Object, icon: String, + chatId: String, + roleId: Number, createdAt: String }); @@ -104,11 +106,15 @@ const send = (url, index) => { emits('disable-input') httpPost(url, { index: index, + src: "chat", message_id: data.value?.["message_id"], message_hash: data.value?.["image"]?.hash, session_id: getSessionId(), key: data.value?.["key"], prompt: data.value?.["prompt"], + chat_id: props.chatId, + role_id: props.roleId, + icon: props.icon, }).then(() => { showNotify({type: "success", message: "任务推送成功,请耐心等待任务执行..."}) loading.value = false diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue index 6ceb6923..b00fe88f 100644 --- a/web/src/views/Home.vue +++ b/web/src/views/Home.vue @@ -118,7 +118,6 @@ const changeNav = (item) => { .content { width: 100%; height: 100vh; - overflow-y: scroll; box-sizing: border-box; } diff --git a/web/src/views/ImageMj.vue b/web/src/views/ImageMj.vue index efa8bdee..1c264e67 100644 --- a/web/src/views/ImageMj.vue +++ b/web/src/views/ImageMj.vue @@ -120,7 +120,7 @@ - + @@ -178,7 +179,8 @@ - + 立即生成 - - + + 任务列表 - - - - + + + + + + + + + 正在加载图片 + + + + + + + + + + + + + + + + 任务正在排队中 + + + + + + + + {{ scope.item.prompt }} + + + + + + + - - + + + 创作记录 - - - - + + + + + + + + + 正在加载图片 + + + + + + + + + + + + + + + + {{ scope.item.prompt }} + + + + + + + @@ -247,23 +332,28 @@ - - - + + + - + + + +