mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
feat: auto translate and rewrite prompt for midjourney and stable-diffusion
This commit is contained in:
parent
342b76f666
commit
b5947545cb
@ -66,7 +66,6 @@ type MidJourneyPlusConfig struct {
|
|||||||
Enabled bool // 如果启用了 MidJourney Plus,将会自动禁用原生的MidJourney服务
|
Enabled bool // 如果启用了 MidJourney Plus,将会自动禁用原生的MidJourney服务
|
||||||
ApiURL string // api 地址
|
ApiURL string // api 地址
|
||||||
Mode string // 绘画模式,可选值:fast/turbo/relax
|
Mode string // 绘画模式,可选值:fast/turbo/relax
|
||||||
CdnURL string // CDN 加速地址
|
|
||||||
ApiKey string
|
ApiKey string
|
||||||
NotifyURL string // 任务进度更新回调地址
|
NotifyURL string // 任务进度更新回调地址
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ type SdTask struct {
|
|||||||
SessionId string `json:"session_id"`
|
SessionId string `json:"session_id"`
|
||||||
Type TaskType `json:"type"`
|
Type TaskType `json:"type"`
|
||||||
UserId int `json:"user_id"`
|
UserId int `json:"user_id"`
|
||||||
Prompt string `json:"prompt,omitempty"`
|
|
||||||
Params SdTaskParams `json:"params"`
|
Params SdTaskParams `json:"params"`
|
||||||
RetryCount int `json:"retry_count"`
|
RetryCount int `json:"retry_count"`
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
|
@ -133,6 +133,7 @@ func (h *SdJobHandler) Image(c *gin.Context) {
|
|||||||
HdScaleAlg: data.HdScaleAlg,
|
HdScaleAlg: data.HdScaleAlg,
|
||||||
HdSteps: data.HdSteps,
|
HdSteps: data.HdSteps,
|
||||||
}
|
}
|
||||||
|
|
||||||
job := model.SdJob{
|
job := model.SdJob{
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
Type: types.TaskImage.String(),
|
Type: types.TaskImage.String(),
|
||||||
@ -153,7 +154,6 @@ func (h *SdJobHandler) Image(c *gin.Context) {
|
|||||||
Id: int(job.Id),
|
Id: int(job.Id),
|
||||||
SessionId: data.SessionId,
|
SessionId: data.SessionId,
|
||||||
Type: types.TaskImage,
|
Type: types.TaskImage,
|
||||||
Prompt: data.Prompt,
|
|
||||||
Params: params,
|
Params: params,
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
})
|
})
|
||||||
|
@ -371,13 +371,6 @@ func main() {
|
|||||||
group.GET("hits", h.Hits)
|
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.Provide(admin.NewFunctionHandler),
|
||||||
fx.Invoke(func(s *core.AppServer, h *admin.FunctionHandler) {
|
fx.Invoke(func(s *core.AppServer, h *admin.FunctionHandler) {
|
||||||
group := s.Engine.Group("/api/admin/function/")
|
group := s.Engine.Group("/api/admin/function/")
|
||||||
|
@ -22,16 +22,7 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(config types.MidJourneyPlusConfig) *Client {
|
func NewClient(config types.MidJourneyPlusConfig) *Client {
|
||||||
var apiURL string
|
return &Client{Config: config, apiURL: config.ApiURL}
|
||||||
if config.CdnURL != "" {
|
|
||||||
apiURL = config.CdnURL
|
|
||||||
} else {
|
|
||||||
apiURL = config.ApiURL
|
|
||||||
}
|
|
||||||
if config.Mode == "" {
|
|
||||||
config.Mode = "fast"
|
|
||||||
}
|
|
||||||
return &Client{Config: config, apiURL: apiURL}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageReq struct {
|
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 res ImageRes
|
||||||
var errRes ErrRes
|
var errRes ErrRes
|
||||||
r, err := req.C().R().
|
r, err := req.C().R().
|
||||||
@ -90,9 +82,7 @@ func (c *Client) Imagine(task types.MjTask) (ImageRes, error) {
|
|||||||
SetErrorResult(&errRes).
|
SetErrorResult(&errRes).
|
||||||
Post(apiURL)
|
Post(apiURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errStr, _ := io.ReadAll(r.Body)
|
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
|
||||||
logger.Errorf("API 返回:%s, API URL: %s", string(errStr), apiURL)
|
|
||||||
return ImageRes{}, fmt.Errorf("请求 API 出错:%v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.IsErrorState() {
|
if r.IsErrorState() {
|
||||||
@ -132,8 +122,7 @@ func (c *Client) Blend(task types.MjTask) (ImageRes, error) {
|
|||||||
SetErrorResult(&errRes).
|
SetErrorResult(&errRes).
|
||||||
Post(apiURL)
|
Post(apiURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errStr, _ := io.ReadAll(r.Body)
|
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
|
||||||
return ImageRes{}, fmt.Errorf("请求 API 出错:%v,%v", err, string(errStr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.IsErrorState() {
|
if r.IsErrorState() {
|
||||||
@ -183,8 +172,7 @@ func (c *Client) SwapFace(task types.MjTask) (ImageRes, error) {
|
|||||||
SetErrorResult(&errRes).
|
SetErrorResult(&errRes).
|
||||||
Post(apiURL)
|
Post(apiURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errStr, _ := io.ReadAll(r.Body)
|
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
|
||||||
return ImageRes{}, fmt.Errorf("请求 API 出错:%v,%v", err, string(errStr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.IsErrorState() {
|
if r.IsErrorState() {
|
||||||
|
@ -167,11 +167,7 @@ func (s *Service) Notify(job model.MidJourneyJob) error {
|
|||||||
job.Progress = utils.IntValue(strings.Replace(task.Progress, "%", "", 1), 0)
|
job.Progress = utils.IntValue(strings.Replace(task.Progress, "%", "", 1), 0)
|
||||||
job.Prompt = task.PromptEn
|
job.Prompt = task.PromptEn
|
||||||
if task.ImageUrl != "" {
|
if task.ImageUrl != "" {
|
||||||
if s.Client.Config.CdnURL != "" {
|
job.OrgURL = task.ImageUrl
|
||||||
job.OrgURL = strings.Replace(task.ImageUrl, s.Client.Config.ApiURL, s.Client.Config.CdnURL, 1)
|
|
||||||
} else {
|
|
||||||
job.OrgURL = task.ImageUrl
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
job.MessageId = task.Id
|
job.MessageId = task.Id
|
||||||
tx := s.db.Updates(&job)
|
tx := s.db.Updates(&job)
|
||||||
|
@ -2,8 +2,11 @@ package mj
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"chatplus/core/types"
|
"chatplus/core/types"
|
||||||
|
"chatplus/service"
|
||||||
"chatplus/store"
|
"chatplus/store"
|
||||||
"chatplus/store/model"
|
"chatplus/store/model"
|
||||||
|
"chatplus/utils"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -62,6 +65,14 @@ func (s *Service) Run() {
|
|||||||
continue
|
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)
|
logger.Infof("%s handle a new MidJourney task: %+v", s.name, task)
|
||||||
switch task.Type {
|
switch task.Type {
|
||||||
case types.TaskImage:
|
case types.TaskImage:
|
||||||
|
@ -2,6 +2,7 @@ package sd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"chatplus/core/types"
|
"chatplus/core/types"
|
||||||
|
"chatplus/service"
|
||||||
"chatplus/service/oss"
|
"chatplus/service/oss"
|
||||||
"chatplus/store"
|
"chatplus/store"
|
||||||
"chatplus/store/model"
|
"chatplus/store/model"
|
||||||
@ -46,6 +47,14 @@ func (s *Service) Run() {
|
|||||||
logger.Errorf("taking task with error: %v", err)
|
logger.Errorf("taking task with error: %v", err)
|
||||||
continue
|
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)
|
logger.Infof("%s handle a new Stable-Diffusion task: %+v", s.name, task)
|
||||||
err = s.Txt2Img(task)
|
err = s.Txt2Img(task)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -66,7 +75,7 @@ func (s *Service) Run() {
|
|||||||
type Txt2ImgReq struct {
|
type Txt2ImgReq struct {
|
||||||
Prompt string `json:"prompt"`
|
Prompt string `json:"prompt"`
|
||||||
NegativePrompt string `json:"negative_prompt"`
|
NegativePrompt string `json:"negative_prompt"`
|
||||||
Seed int64 `json:"seed"`
|
Seed int64 `json:"seed,omitempty"`
|
||||||
Steps int `json:"steps"`
|
Steps int `json:"steps"`
|
||||||
CfgScale float32 `json:"cfg_scale"`
|
CfgScale float32 `json:"cfg_scale"`
|
||||||
Width int `json:"width"`
|
Width int `json:"width"`
|
||||||
|
4
api/service/types.go
Normal file
4
api/service/types.go
Normal 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]"
|
@ -3,15 +3,10 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"chatplus/core/types"
|
"chatplus/core/types"
|
||||||
logger2 "chatplus/logger"
|
logger2 "chatplus/logger"
|
||||||
"chatplus/store/model"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"github.com/imroc/req/v3"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger = logger2.GetLogger()
|
var logger = logger2.GetLogger()
|
||||||
@ -66,64 +61,3 @@ func DownloadImage(imageURL string, proxy string) ([]byte, error) {
|
|||||||
|
|
||||||
return imageBytes, nil
|
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
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"chatplus/core/types"
|
||||||
|
"chatplus/store/model"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/imroc/req/v3"
|
||||||
"github.com/pkoukk/tiktoken-go"
|
"github.com/pkoukk/tiktoken-go"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CalcTokens(text string, model string) (int, error) {
|
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)
|
token := tke.Encode(text, nil, nil)
|
||||||
return len(token), 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
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
@ -94,6 +95,7 @@ func InterfaceToString(value interface{}) string {
|
|||||||
return JsonEncode(value)
|
return JsonEncode(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CutWords 截取前 N 个单词
|
||||||
func CutWords(str string, num int) string {
|
func CutWords(str string, num int) string {
|
||||||
// 按空格分割字符串为单词切片
|
// 按空格分割字符串为单词切片
|
||||||
words := strings.Fields(str)
|
words := strings.Fields(str)
|
||||||
@ -105,3 +107,13 @@ func CutWords(str string, num int) string {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasChinese 判断文本是否含有中文
|
||||||
|
func HasChinese(text string) bool {
|
||||||
|
for _, char := range text {
|
||||||
|
if unicode.Is(unicode.Scripts["Han"], char) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -244,7 +244,7 @@
|
|||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {nextTick, onMounted, ref} from 'vue'
|
import {nextTick, onMounted, onUnmounted, ref} from 'vue'
|
||||||
import ChatPrompt from "@/components/ChatPrompt.vue";
|
import ChatPrompt from "@/components/ChatPrompt.vue";
|
||||||
import ChatReply from "@/components/ChatReply.vue";
|
import ChatReply from "@/components/ChatReply.vue";
|
||||||
import {
|
import {
|
||||||
@ -339,6 +339,10 @@ onMounted(() => {
|
|||||||
window.onresize = () => resizeElement();
|
window.onresize = () => resizeElement();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
socket.value = null
|
||||||
|
})
|
||||||
|
|
||||||
// 初始化数据
|
// 初始化数据
|
||||||
const initData = () => {
|
const initData = () => {
|
||||||
// 检查会话
|
// 检查会话
|
||||||
@ -699,12 +703,11 @@ const connect = function (chat_id, role_id) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_socket.addEventListener('close', () => {
|
_socket.addEventListener('close', () => {
|
||||||
if (activelyClose.value) { // 忽略主动关闭
|
if (activelyClose.value || socket.value === null) { // 忽略主动关闭
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 停止发送消息
|
// 停止发送消息
|
||||||
disableInput(true)
|
disableInput(true)
|
||||||
socket.value = null;
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
checkSession().then(() => {
|
checkSession().then(() => {
|
||||||
connect(chat_id, role_id)
|
connect(chat_id, role_id)
|
||||||
|
@ -218,36 +218,13 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<div class="param-line pt">
|
<div class="param-line pt">
|
||||||
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||||
ref="promptRef"
|
ref="promptRef"
|
||||||
placeholder="这里输入你的英文咒语,例如:A chinese girl walking in the middle of a cobblestone street"/>
|
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="param-line pt">
|
<div class="param-line pt">
|
||||||
@ -260,19 +237,13 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<div class="param-line pt">
|
<div class="param-line pt">
|
||||||
<el-input v-model="params.neg_prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
<el-input v-model="params.neg_prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
|
||||||
ref="promptRef"
|
ref="promptRef"
|
||||||
placeholder="这里输入你不希望出现在图片上的内容,元素"/>
|
placeholder="请在此输入你不希望出现在图片上的内容,系统会自动翻译中文提示词"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@ -471,7 +442,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {nextTick, onMounted, onUnmounted, ref} from "vue"
|
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 Compressor from "compressorjs";
|
||||||
import {httpGet, httpPost} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
||||||
@ -560,50 +531,9 @@ const finishedJobs = ref([])
|
|||||||
|
|
||||||
const socket = ref(null)
|
const socket = ref(null)
|
||||||
const power = ref(0)
|
const power = ref(0)
|
||||||
const translating = ref(false)
|
|
||||||
const userId = ref(0)
|
const userId = ref(0)
|
||||||
const isLogin = ref(false)
|
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 heartbeatHandle = ref(null)
|
||||||
const connect = () => {
|
const connect = () => {
|
||||||
let host = process.env.VUE_APP_WS_HOST
|
let host = process.env.VUE_APP_WS_HOST
|
||||||
@ -646,7 +576,9 @@ const connect = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_socket.addEventListener('close', () => {
|
_socket.addEventListener('close', () => {
|
||||||
connect()
|
if (socket.value !== null) {
|
||||||
|
connect()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,6 +595,10 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
socket.value = null
|
||||||
|
})
|
||||||
|
|
||||||
// 初始化数据
|
// 初始化数据
|
||||||
const initData = () => {
|
const initData = () => {
|
||||||
checkSession().then(user => {
|
checkSession().then(user => {
|
||||||
|
@ -117,26 +117,6 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</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">
|
<div class="param-line">
|
||||||
<el-form-item label="高清修复">
|
<el-form-item label="高清修复">
|
||||||
<template #default>
|
<template #default>
|
||||||
@ -248,34 +228,10 @@
|
|||||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
ref="promptRef"
|
ref="promptRef"
|
||||||
placeholder="正向提示词,例如:A chinese girl walking in the middle of a cobblestone street"
|
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<div class="param-line pt">
|
||||||
<span>反向提示词:</span>
|
<span>反向提示词:</span>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
@ -516,7 +472,7 @@
|
|||||||
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
|
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false" @success="initData"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -553,14 +509,13 @@ const params = ref({
|
|||||||
height: 1024,
|
height: 1024,
|
||||||
sampler: samplers[0],
|
sampler: samplers[0],
|
||||||
seed: -1,
|
seed: -1,
|
||||||
steps: 30,
|
steps: 20,
|
||||||
cfg_scale: 7,
|
cfg_scale: 7,
|
||||||
face_fix: false,
|
|
||||||
hd_fix: false,
|
hd_fix: false,
|
||||||
hd_redraw_rate: 0.5,
|
hd_redraw_rate: 0.7,
|
||||||
hd_scale: 2,
|
hd_scale: 2,
|
||||||
hd_scale_alg: scaleAlg[0],
|
hd_scale_alg: scaleAlg[0],
|
||||||
hd_steps: 15,
|
hd_steps: 0,
|
||||||
prompt: "",
|
prompt: "",
|
||||||
negative_prompt: "nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet",
|
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)
|
params.value = JSON.parse(_params)
|
||||||
}
|
}
|
||||||
const power = ref(0)
|
const power = ref(0)
|
||||||
|
const sdPower = ref(0) // 画一张 SD 图片消耗算力
|
||||||
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 socket = ref(null)
|
const socket = ref(null)
|
||||||
const userId = ref(0)
|
const userId = ref(0)
|
||||||
@ -651,7 +575,9 @@ const connect = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_socket.addEventListener('close', () => {
|
_socket.addEventListener('close', () => {
|
||||||
connect()
|
if (socket.value !== null) {
|
||||||
|
connect()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,10 +592,17 @@ onMounted(() => {
|
|||||||
clipboard.value.on('error', () => {
|
clipboard.value.on('error', () => {
|
||||||
ElMessage.error('复制失败!');
|
ElMessage.error('复制失败!');
|
||||||
})
|
})
|
||||||
|
|
||||||
|
httpGet("/api/config/get?key=system").then(res => {
|
||||||
|
sdPower.value = res.data["sd_power"]
|
||||||
|
}).catch(e => {
|
||||||
|
ElMessage.error("获取系统配置失败:" + e.message)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clipboard.value.destroy()
|
clipboard.value.destroy()
|
||||||
|
socket.value = null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -699,7 +632,7 @@ const fetchRunningJobs = (userId) => {
|
|||||||
message: `任务ID:${jobs[i]['task_id']}<br />原因:${jobs[i]['err_msg']}`,
|
message: `任务ID:${jobs[i]['task_id']}<br />原因:${jobs[i]['err_msg']}`,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
power.value += 1
|
power.value += sdPower.value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_jobs.push(jobs[i])
|
_jobs.push(jobs[i])
|
||||||
@ -761,7 +694,7 @@ const generate = () => {
|
|||||||
params.value.session_id = getSessionId()
|
params.value.session_id = getSessionId()
|
||||||
httpPost("/api/sd/image", params.value).then(() => {
|
httpPost("/api/sd/image", params.value).then(() => {
|
||||||
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
||||||
power.value -= 1
|
power.value -= sdPower.value
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("任务推送失败:" + e.message)
|
ElMessage.error("任务推送失败:" + e.message)
|
||||||
})
|
})
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {nextTick, onMounted, ref} from "vue";
|
import {nextTick, onMounted, onUnmounted, ref} from "vue";
|
||||||
import {showImagePreview, showNotify, showToast} from "vant";
|
import {showImagePreview, showNotify, showToast} from "vant";
|
||||||
import {onBeforeRouteLeave, useRouter} from "vue-router";
|
import {onBeforeRouteLeave, useRouter} from "vue-router";
|
||||||
import {dateFormat, processContent, randString, renderInputText, UUID} from "@/utils/libs";
|
import {dateFormat, processContent, randString, renderInputText, UUID} from "@/utils/libs";
|
||||||
@ -147,6 +147,10 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
socket.value = null
|
||||||
|
})
|
||||||
|
|
||||||
const chatData = ref([])
|
const chatData = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const finished = ref(false)
|
const finished = ref(false)
|
||||||
@ -347,12 +351,11 @@ const connect = function (chat_id, role_id) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_socket.addEventListener('close', () => {
|
_socket.addEventListener('close', () => {
|
||||||
if (activelyClose.value) { // 忽略主动关闭
|
if (activelyClose.value || socket.value === null) { // 忽略主动关闭
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 停止发送消息
|
// 停止发送消息
|
||||||
canSend.value = true;
|
canSend.value = true;
|
||||||
socket.value = null;
|
|
||||||
// 重连
|
// 重连
|
||||||
checkSession().then(() => {
|
checkSession().then(() => {
|
||||||
connect(chat_id, role_id)
|
connect(chat_id, role_id)
|
||||||
|
@ -67,13 +67,7 @@
|
|||||||
label="提示词"
|
label="提示词"
|
||||||
autosize
|
autosize
|
||||||
type="textarea"
|
type="textarea"
|
||||||
placeholder="如:一个美丽的中国女孩站在电影院门口,手上拿着爆米花,微笑,写实风格,电影灯光效果,半身像">
|
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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<van-collapse v-model="activeColspan">
|
<van-collapse v-model="activeColspan">
|
||||||
@ -206,7 +200,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {nextTick, onMounted, ref} from "vue";
|
import {nextTick, onMounted, onUnmounted, ref} from "vue";
|
||||||
import {
|
import {
|
||||||
showConfirmDialog,
|
showConfirmDialog,
|
||||||
showFailToast,
|
showFailToast,
|
||||||
@ -221,9 +215,8 @@ import Compressor from "compressorjs";
|
|||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
import {getSessionId} from "@/store/session";
|
import {getSessionId} from "@/store/session";
|
||||||
import {checkSession} from "@/action/session";
|
import {checkSession} from "@/action/session";
|
||||||
import Clipboard from "clipboard";
|
|
||||||
import {useRouter} from "vue-router";
|
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 title = ref('MidJourney 绘画')
|
||||||
const activeColspan = ref([""])
|
const activeColspan = ref([""])
|
||||||
@ -281,6 +274,10 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
socket.value = null
|
||||||
|
})
|
||||||
|
|
||||||
const heartbeatHandle = ref(null)
|
const heartbeatHandle = ref(null)
|
||||||
const connect = () => {
|
const connect = () => {
|
||||||
let host = process.env.VUE_APP_WS_HOST
|
let host = process.env.VUE_APP_WS_HOST
|
||||||
@ -315,13 +312,15 @@ const connect = () => {
|
|||||||
|
|
||||||
_socket.addEventListener('message', event => {
|
_socket.addEventListener('message', event => {
|
||||||
if (event.data instanceof Blob) {
|
if (event.data instanceof Blob) {
|
||||||
fetchRunningJobs(userId.value)
|
fetchRunningJobs()
|
||||||
fetchFinishJobs(userId.value)
|
fetchFinishJobs(1)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_socket.addEventListener('close', () => {
|
_socket.addEventListener('close', () => {
|
||||||
connect()
|
if (socket.value !== null) {
|
||||||
|
connect()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,24 +511,6 @@ const showPrompt = (item) => {
|
|||||||
const imageView = (item) => {
|
const imageView = (item) => {
|
||||||
showImagePreview([item['img_url']]);
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus">
|
<style lang="stylus">
|
||||||
|
Loading…
Reference in New Issue
Block a user