feat: auto translate and rewrite prompt for midjourney and stable-diffusion

This commit is contained in:
RockYang 2024-03-27 13:45:52 +08:00
parent 342b76f666
commit b5947545cb
18 changed files with 162 additions and 355 deletions

View File

@ -66,7 +66,6 @@ type MidJourneyPlusConfig struct {
Enabled bool // 如果启用了 MidJourney Plus将会自动禁用原生的MidJourney服务
ApiURL string // api 地址
Mode string // 绘画模式可选值fast/turbo/relax
CdnURL string // CDN 加速地址
ApiKey string
NotifyURL string // 任务进度更新回调地址
}

View File

@ -36,7 +36,6 @@ type SdTask struct {
SessionId string `json:"session_id"`
Type TaskType `json:"type"`
UserId int `json:"user_id"`
Prompt string `json:"prompt,omitempty"`
Params SdTaskParams `json:"params"`
RetryCount int `json:"retry_count"`
}

View File

@ -1,60 +0,0 @@
package handler
import (
"chatplus/core"
"chatplus/core/types"
"chatplus/utils"
"chatplus/utils/resp"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
const rewritePromptTemplate = "Please rewrite the following text into AI painting prompt words, and please try to add detailed description of the picture, painting style, scene, rendering effect, picture light and other elements. Please output directly in English without any explanation, within 150 words. The text to be rewritten is: [%s]"
const translatePromptTemplate = "Translate the following painting prompt words into English keyword phrases. Without any explanation, directly output the keyword phrases separated by commas. The content to be translated is: [%s]"
type PromptHandler struct {
BaseHandler
}
func NewPromptHandler(app *core.AppServer, db *gorm.DB) *PromptHandler {
return &PromptHandler{BaseHandler: BaseHandler{App: app, DB: db}}
}
// Rewrite translate and rewrite prompt with ChatGPT
func (h *PromptHandler) Rewrite(c *gin.Context) {
var data struct {
Prompt string `json:"prompt"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(rewritePromptTemplate, data.Prompt))
if err != nil {
resp.ERROR(c, err.Error())
return
}
resp.SUCCESS(c, content)
}
func (h *PromptHandler) Translate(c *gin.Context) {
var data struct {
Prompt string `json:"prompt"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(translatePromptTemplate, data.Prompt))
if err != nil {
resp.ERROR(c, err.Error())
return
}
resp.SUCCESS(c, content)
}

View File

@ -133,6 +133,7 @@ func (h *SdJobHandler) Image(c *gin.Context) {
HdScaleAlg: data.HdScaleAlg,
HdSteps: data.HdSteps,
}
job := model.SdJob{
UserId: userId,
Type: types.TaskImage.String(),
@ -153,7 +154,6 @@ func (h *SdJobHandler) Image(c *gin.Context) {
Id: int(job.Id),
SessionId: data.SessionId,
Type: types.TaskImage,
Prompt: data.Prompt,
Params: params,
UserId: userId,
})

View File

@ -371,13 +371,6 @@ func main() {
group.GET("hits", h.Hits)
}),
fx.Provide(handler.NewPromptHandler),
fx.Invoke(func(s *core.AppServer, h *handler.PromptHandler) {
group := s.Engine.Group("/api/prompt/")
group.POST("rewrite", h.Rewrite)
group.POST("translate", h.Translate)
}),
fx.Provide(admin.NewFunctionHandler),
fx.Invoke(func(s *core.AppServer, h *admin.FunctionHandler) {
group := s.Engine.Group("/api/admin/function/")

View File

@ -22,16 +22,7 @@ type Client struct {
}
func NewClient(config types.MidJourneyPlusConfig) *Client {
var apiURL string
if config.CdnURL != "" {
apiURL = config.CdnURL
} else {
apiURL = config.ApiURL
}
if config.Mode == "" {
config.Mode = "fast"
}
return &Client{Config: config, apiURL: apiURL}
return &Client{Config: config, apiURL: config.ApiURL}
}
type ImageReq struct {
@ -81,6 +72,7 @@ func (c *Client) Imagine(task types.MjTask) (ImageRes, error) {
}
}
logger.Info("API URL: ", apiURL)
var res ImageRes
var errRes ErrRes
r, err := req.C().R().
@ -90,9 +82,7 @@ func (c *Client) Imagine(task types.MjTask) (ImageRes, error) {
SetErrorResult(&errRes).
Post(apiURL)
if err != nil {
errStr, _ := io.ReadAll(r.Body)
logger.Errorf("API 返回:%s, API URL: %s", string(errStr), apiURL)
return ImageRes{}, fmt.Errorf("请求 API 出错:%v", err)
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
}
if r.IsErrorState() {
@ -132,8 +122,7 @@ func (c *Client) Blend(task types.MjTask) (ImageRes, error) {
SetErrorResult(&errRes).
Post(apiURL)
if err != nil {
errStr, _ := io.ReadAll(r.Body)
return ImageRes{}, fmt.Errorf("请求 API 出错:%v%v", err, string(errStr))
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
}
if r.IsErrorState() {
@ -183,8 +172,7 @@ func (c *Client) SwapFace(task types.MjTask) (ImageRes, error) {
SetErrorResult(&errRes).
Post(apiURL)
if err != nil {
errStr, _ := io.ReadAll(r.Body)
return ImageRes{}, fmt.Errorf("请求 API 出错:%v%v", err, string(errStr))
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
}
if r.IsErrorState() {

View File

@ -167,12 +167,8 @@ func (s *Service) Notify(job model.MidJourneyJob) error {
job.Progress = utils.IntValue(strings.Replace(task.Progress, "%", "", 1), 0)
job.Prompt = task.PromptEn
if task.ImageUrl != "" {
if s.Client.Config.CdnURL != "" {
job.OrgURL = strings.Replace(task.ImageUrl, s.Client.Config.ApiURL, s.Client.Config.CdnURL, 1)
} else {
job.OrgURL = task.ImageUrl
}
}
job.MessageId = task.Id
tx := s.db.Updates(&job)
if tx.Error != nil {

View File

@ -2,8 +2,11 @@ package mj
import (
"chatplus/core/types"
"chatplus/service"
"chatplus/store"
"chatplus/store/model"
"chatplus/utils"
"fmt"
"strings"
"sync/atomic"
"time"
@ -62,6 +65,14 @@ func (s *Service) Run() {
continue
}
// 翻译提示词
if utils.HasChinese(task.Prompt) {
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt))
if err == nil {
task.Prompt = content
}
}
logger.Infof("%s handle a new MidJourney task: %+v", s.name, task)
switch task.Type {
case types.TaskImage:

View File

@ -2,6 +2,7 @@ package sd
import (
"chatplus/core/types"
"chatplus/service"
"chatplus/service/oss"
"chatplus/store"
"chatplus/store/model"
@ -46,6 +47,14 @@ func (s *Service) Run() {
logger.Errorf("taking task with error: %v", err)
continue
}
// 翻译提示词
if utils.HasChinese(task.Params.Prompt) {
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, task.Params.Prompt))
if err == nil {
task.Params.Prompt = content
}
}
logger.Infof("%s handle a new Stable-Diffusion task: %+v", s.name, task)
err = s.Txt2Img(task)
if err != nil {
@ -66,7 +75,7 @@ func (s *Service) Run() {
type Txt2ImgReq struct {
Prompt string `json:"prompt"`
NegativePrompt string `json:"negative_prompt"`
Seed int64 `json:"seed"`
Seed int64 `json:"seed,omitempty"`
Steps int `json:"steps"`
CfgScale float32 `json:"cfg_scale"`
Width int `json:"width"`

4
api/service/types.go Normal file
View File

@ -0,0 +1,4 @@
package service
const RewritePromptTemplate = "Please rewrite the following text into AI painting prompt words, and please try to add detailed description of the picture, painting style, scene, rendering effect, picture light and other elements. Please output directly in English without any explanation, within 150 words. The text to be rewritten is: [%s]"
const TranslatePromptTemplate = "Translate the following painting prompt words into English keyword phrases. Without any explanation, directly output the keyword phrases separated by commas. The content to be translated is: [%s]"

View File

@ -3,15 +3,10 @@ package utils
import (
"chatplus/core/types"
logger2 "chatplus/logger"
"chatplus/store/model"
"encoding/json"
"fmt"
"github.com/imroc/req/v3"
"gorm.io/gorm"
"io"
"net/http"
"net/url"
"time"
)
var logger = logger2.GetLogger()
@ -66,64 +61,3 @@ func DownloadImage(imageURL string, proxy string) ([]byte, error) {
return imageBytes, nil
}
type apiRes struct {
Model string `json:"model"`
Choices []struct {
Index int `json:"index"`
Message struct {
Role string `json:"role"`
Content string `json:"content"`
} `json:"message"`
FinishReason string `json:"finish_reason"`
} `json:"choices"`
}
type apiErrRes struct {
Error struct {
Code interface{} `json:"code"`
Message string `json:"message"`
Param interface{} `json:"param"`
Type string `json:"type"`
} `json:"error"`
}
func OpenAIRequest(db *gorm.DB, prompt string) (string, error) {
var apiKey model.ApiKey
res := db.Where("platform = ?", types.OpenAI).Where("type = ?", "chat").Where("enabled = ?", true).First(&apiKey)
if res.Error != nil {
return "", fmt.Errorf("error with fetch OpenAI API KEY%v", res.Error)
}
messages := make([]interface{}, 1)
messages[0] = types.Message{
Role: "user",
Content: prompt,
}
var response apiRes
var errRes apiErrRes
client := req.C()
if apiKey.ProxyURL != "" {
client.SetProxyURL(apiKey.ApiURL)
}
r, err := client.R().SetHeader("Content-Type", "application/json").
SetHeader("Authorization", "Bearer "+apiKey.Value).
SetBody(types.ApiRequest{
Model: "gpt-3.5-turbo-0125",
Temperature: 0.9,
MaxTokens: 1024,
Stream: false,
Messages: messages,
}).
SetErrorResult(&errRes).
SetSuccessResult(&response).Post(apiKey.ApiURL)
if err != nil || r.IsErrorState() {
return "", fmt.Errorf("error with http request: %v%v%s", err, r.Err, errRes.Error.Message)
}
// 更新 API KEY 的最后使用时间
db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
return response.Choices[0].Message.Content, nil
}

View File

@ -1,8 +1,13 @@
package utils
import (
"chatplus/core/types"
"chatplus/store/model"
"fmt"
"github.com/imroc/req/v3"
"github.com/pkoukk/tiktoken-go"
"gorm.io/gorm"
"time"
)
func CalcTokens(text string, model string) (int, error) {
@ -18,3 +23,64 @@ func CalcTokens(text string, model string) (int, error) {
token := tke.Encode(text, nil, nil)
return len(token), nil
}
type apiRes struct {
Model string `json:"model"`
Choices []struct {
Index int `json:"index"`
Message struct {
Role string `json:"role"`
Content string `json:"content"`
} `json:"message"`
FinishReason string `json:"finish_reason"`
} `json:"choices"`
}
type apiErrRes struct {
Error struct {
Code interface{} `json:"code"`
Message string `json:"message"`
Param interface{} `json:"param"`
Type string `json:"type"`
} `json:"error"`
}
func OpenAIRequest(db *gorm.DB, prompt string) (string, error) {
var apiKey model.ApiKey
res := db.Where("platform = ?", types.OpenAI).Where("type = ?", "chat").Where("enabled = ?", true).First(&apiKey)
if res.Error != nil {
return "", fmt.Errorf("error with fetch OpenAI API KEY%v", res.Error)
}
messages := make([]interface{}, 1)
messages[0] = types.Message{
Role: "user",
Content: prompt,
}
var response apiRes
var errRes apiErrRes
client := req.C()
if apiKey.ProxyURL != "" {
client.SetProxyURL(apiKey.ApiURL)
}
r, err := client.R().SetHeader("Content-Type", "application/json").
SetHeader("Authorization", "Bearer "+apiKey.Value).
SetBody(types.ApiRequest{
Model: "gpt-3.5-turbo-0125",
Temperature: 0.9,
MaxTokens: 1024,
Stream: false,
Messages: messages,
}).
SetErrorResult(&errRes).
SetSuccessResult(&response).Post(apiKey.ApiURL)
if err != nil || r.IsErrorState() {
return "", fmt.Errorf("error with http request: %v%v%s", err, r.Err, errRes.Error.Message)
}
// 更新 API KEY 的最后使用时间
db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
return response.Choices[0].Message.Content, nil
}

View File

@ -6,6 +6,7 @@ import (
"math/rand"
"strings"
"time"
"unicode"
"golang.org/x/crypto/sha3"
)
@ -94,6 +95,7 @@ func InterfaceToString(value interface{}) string {
return JsonEncode(value)
}
// CutWords 截取前 N 个单词
func CutWords(str string, num int) string {
// 按空格分割字符串为单词切片
words := strings.Fields(str)
@ -105,3 +107,13 @@ func CutWords(str string, num int) string {
return str
}
}
// HasChinese 判断文本是否含有中文
func HasChinese(text string) bool {
for _, char := range text {
if unicode.Is(unicode.Scripts["Han"], char) {
return true
}
}
return false
}

View File

@ -244,7 +244,7 @@
</template>
<script setup>
import {nextTick, onMounted, ref} from 'vue'
import {nextTick, onMounted, onUnmounted, ref} from 'vue'
import ChatPrompt from "@/components/ChatPrompt.vue";
import ChatReply from "@/components/ChatReply.vue";
import {
@ -339,6 +339,10 @@ onMounted(() => {
window.onresize = () => resizeElement();
});
onUnmounted(() => {
socket.value = null
})
//
const initData = () => {
//
@ -699,12 +703,11 @@ const connect = function (chat_id, role_id) {
});
_socket.addEventListener('close', () => {
if (activelyClose.value) { //
if (activelyClose.value || socket.value === null) { //
return;
}
//
disableInput(true)
socket.value = null;
loading.value = true;
checkSession().then(() => {
connect(chat_id, role_id)

View File

@ -218,36 +218,13 @@
</el-icon>
</el-tooltip>
</div>
<div>
<el-button type="primary" @click="translatePrompt(false)" :disabled="translating">
<el-icon style="margin-right: 6px;font-size: 18px;">
<Refresh/>
</el-icon>
翻译
</el-button>
<el-tooltip
class="box-item"
effect="light"
raw-content
content="使用 AI 翻译并重写提示词,<br/>增加更多细节,风格等描述"
placement="top-end"
>
<el-button type="success" @click="rewritePrompt" :disabled="translating">
<el-icon style="margin-right: 6px;font-size: 18px;">
<Refresh/>
</el-icon>
翻译并重写
</el-button>
</el-tooltip>
</div>
</div>
</div>
<div class="param-line pt">
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
ref="promptRef"
placeholder="这里输入你的英文咒语例如A chinese girl walking in the middle of a cobblestone street"/>
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
</div>
<div class="param-line pt">
@ -260,19 +237,13 @@
</el-icon>
</el-tooltip>
</div>
<el-button type="primary" @click="translatePrompt(true)" :disabled="translating">
<el-icon style="margin-right: 6px;font-size: 18px;">
<Refresh/>
</el-icon>
翻译
</el-button>
</div>
</div>
<div class="param-line pt">
<el-input v-model="params.neg_prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
ref="promptRef"
placeholder="这里输入你不希望出现在图片上的内容,元素"/>
placeholder="请在此输入你不希望出现在图片上的内容,系统会自动翻译中文提示词"/>
</div>
</div>
</el-tab-pane>
@ -471,7 +442,7 @@
<script setup>
import {nextTick, onMounted, onUnmounted, ref} from "vue"
import {ChromeFilled, Delete, DocumentCopy, InfoFilled, Picture, Plus, Refresh} from "@element-plus/icons-vue";
import {ChromeFilled, Delete, DocumentCopy, InfoFilled, Picture, Plus} from "@element-plus/icons-vue";
import Compressor from "compressorjs";
import {httpGet, httpPost} from "@/utils/http";
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
@ -560,50 +531,9 @@ const finishedJobs = ref([])
const socket = ref(null)
const power = ref(0)
const translating = ref(false)
const userId = ref(0)
const isLogin = ref(false)
const rewritePrompt = () => {
if (!isLogin.value) {
showLoginDialog.value = true
return
}
translating.value = true
httpPost("/api/prompt/rewrite", {"prompt": params.value.prompt}).then(res => {
params.value.prompt = res.data
translating.value = false
}).catch(e => {
translating.value = false
ElMessage.error("翻译失败:" + e.message)
})
}
const translatePrompt = (negative) => {
if (!isLogin.value) {
showLoginDialog.value = true
return
}
translating.value = true
let prompt = params.value.prompt
if (negative) {
prompt = params.value.neg_prompt
}
httpPost("/api/prompt/translate", {"prompt": prompt}).then(res => {
if (negative) {
params.value.neg_prompt = res.data
} else {
params.value.prompt = res.data
}
translating.value = false
}).catch(e => {
translating.value = false
ElMessage.error("翻译失败:" + e.message)
})
}
const heartbeatHandle = ref(null)
const connect = () => {
let host = process.env.VUE_APP_WS_HOST
@ -646,7 +576,9 @@ const connect = () => {
});
_socket.addEventListener('close', () => {
if (socket.value !== null) {
connect()
}
});
}
@ -663,6 +595,10 @@ onMounted(() => {
})
})
onUnmounted(() => {
socket.value = null
})
//
const initData = () => {
checkSession().then(user => {

View File

@ -117,26 +117,6 @@
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="面部修复">
<template #default>
<div class="form-item-inner">
<el-switch v-model="params.face_fix" style="--el-switch-on-color: #47fff1;"/>
<el-tooltip
effect="light"
content="仅对绘制人物图像有效果。"
raw-content
placement="right"
>
<el-icon style="margin-top: 6px">
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="高清修复">
<template #default>
@ -248,34 +228,10 @@
:autosize="{ minRows: 4, maxRows: 6 }"
type="textarea"
ref="promptRef"
placeholder="正向提示词例如A chinese girl walking in the middle of a cobblestone street"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
/>
</div>
<div style="padding: 10px">
<el-button type="primary" @click="translatePrompt" size="small">
<el-icon style="margin-right: 6px;font-size: 18px;">
<Refresh/>
</el-icon>
翻译
</el-button>
<el-tooltip
class="box-item"
effect="dark"
raw-content
content="使用 AI 翻译并重写提示词,<br/>增加更多细节,风格等描述"
placement="top-end"
>
<el-button type="success" @click="rewritePrompt" size="small">
<el-icon style="margin-right: 6px;font-size: 18px;">
<Refresh/>
</el-icon>
翻译并重写
</el-button>
</el-tooltip>
</div>
<div class="param-line pt">
<span>反向提示词</span>
<el-tooltip
@ -553,14 +509,13 @@ const params = ref({
height: 1024,
sampler: samplers[0],
seed: -1,
steps: 30,
steps: 20,
cfg_scale: 7,
face_fix: false,
hd_fix: false,
hd_redraw_rate: 0.5,
hd_redraw_rate: 0.7,
hd_scale: 2,
hd_scale_alg: scaleAlg[0],
hd_steps: 15,
hd_steps: 0,
prompt: "",
negative_prompt: "nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet",
})
@ -574,38 +529,7 @@ if (_params) {
params.value = JSON.parse(_params)
}
const power = ref(0)
const rewritePrompt = () => {
if (!isLogin.value) {
showLoginDialog.value = true
return
}
translating.value = true
httpPost("/api/prompt/rewrite", {"prompt": params.value.prompt}).then(res => {
params.value.prompt = res.data
translating.value = false
}).catch(e => {
translating.value = false
ElMessage.error("翻译失败:" + e.message)
})
}
const translatePrompt = () => {
if (!isLogin.value) {
showLoginDialog.value = true
return
}
translating.value = true
httpPost("/api/prompt/translate", {"prompt": params.value.prompt}).then(res => {
params.value.prompt = res.data
translating.value = false
}).catch(e => {
translating.value = false
ElMessage.error("翻译失败:" + e.message)
})
}
const sdPower = ref(0) // SD
const socket = ref(null)
const userId = ref(0)
@ -651,7 +575,9 @@ const connect = () => {
});
_socket.addEventListener('close', () => {
if (socket.value !== null) {
connect()
}
});
}
@ -666,10 +592,17 @@ onMounted(() => {
clipboard.value.on('error', () => {
ElMessage.error('复制失败!');
})
httpGet("/api/config/get?key=system").then(res => {
sdPower.value = res.data["sd_power"]
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
})
onUnmounted(() => {
clipboard.value.destroy()
socket.value = null
})
@ -699,7 +632,7 @@ const fetchRunningJobs = (userId) => {
message: `任务ID${jobs[i]['task_id']}<br />原因:${jobs[i]['err_msg']}`,
type: 'error',
})
power.value += 1
power.value += sdPower.value
continue
}
_jobs.push(jobs[i])
@ -761,7 +694,7 @@ const generate = () => {
params.value.session_id = getSessionId()
httpPost("/api/sd/image", params.value).then(() => {
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
power.value -= 1
power.value -= sdPower.value
}).catch(e => {
ElMessage.error("任务推送失败:" + e.message)
})

View File

@ -103,7 +103,7 @@
</template>
<script setup>
import {nextTick, onMounted, ref} from "vue";
import {nextTick, onMounted, onUnmounted, ref} from "vue";
import {showImagePreview, showNotify, showToast} from "vant";
import {onBeforeRouteLeave, useRouter} from "vue-router";
import {dateFormat, processContent, randString, renderInputText, UUID} from "@/utils/libs";
@ -147,6 +147,10 @@ onMounted(() => {
})
})
onUnmounted(() => {
socket.value = null
})
const chatData = ref([])
const loading = ref(false)
const finished = ref(false)
@ -347,12 +351,11 @@ const connect = function (chat_id, role_id) {
});
_socket.addEventListener('close', () => {
if (activelyClose.value) { //
if (activelyClose.value || socket.value === null) { //
return;
}
//
canSend.value = true;
socket.value = null;
//
checkSession().then(() => {
connect(chat_id, role_id)

View File

@ -67,13 +67,7 @@
label="提示词"
autosize
type="textarea"
placeholder="如:一个美丽的中国女孩站在电影院门口,手上拿着爆米花,微笑,写实风格,电影灯光效果,半身像">
<template #button>
<van-button v-if="translating" disabled loading type="primary"/>
<van-button v-else size="small" type="primary" @click="translatePrompt">翻译</van-button>
</template>
</van-field>
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
</div>
<van-collapse v-model="activeColspan">
@ -206,7 +200,7 @@
</template>
<script setup>
import {nextTick, onMounted, ref} from "vue";
import {nextTick, onMounted, onUnmounted, ref} from "vue";
import {
showConfirmDialog,
showFailToast,
@ -221,9 +215,8 @@ import Compressor from "compressorjs";
import {ElMessage} from "element-plus";
import {getSessionId} from "@/store/session";
import {checkSession} from "@/action/session";
import Clipboard from "clipboard";
import {useRouter} from "vue-router";
import {Delete, Picture} from "@element-plus/icons-vue";
import {Delete} from "@element-plus/icons-vue";
const title = ref('MidJourney 绘画')
const activeColspan = ref([""])
@ -281,6 +274,10 @@ onMounted(() => {
});
})
onUnmounted(() => {
socket.value = null
})
const heartbeatHandle = ref(null)
const connect = () => {
let host = process.env.VUE_APP_WS_HOST
@ -315,13 +312,15 @@ const connect = () => {
_socket.addEventListener('message', event => {
if (event.data instanceof Blob) {
fetchRunningJobs(userId.value)
fetchFinishJobs(userId.value)
fetchRunningJobs()
fetchFinishJobs(1)
}
});
_socket.addEventListener('close', () => {
if (socket.value !== null) {
connect()
}
});
}
@ -512,24 +511,6 @@ const showPrompt = (item) => {
const imageView = (item) => {
showImagePreview([item['img_url']]);
}
const translating = ref(false)
const translatePrompt = () => {
if (params.value.prompt === '') {
return showToast("请输入中文提示词!")
}
translating.value = true
const prompt = params.value.prompt
httpPost("/api/prompt/translate", {"prompt": prompt}).then(res => {
params.value.prompt = res.data
translating.value = false
}).catch(e => {
translating.value = false
showFailToast("翻译失败:" + e.message)
})
}
</script>
<style lang="stylus">