feat: support midjourney --cref and --sref for role consistency

This commit is contained in:
RockYang 2024-04-02 14:59:53 +08:00
parent 56c225bf20
commit 3f1ad4b7dc
10 changed files with 247 additions and 53 deletions

View File

@ -1,7 +1,10 @@
# 更新日志 # 更新日志
## 4.0.2 ## 4.0.2
* 功能新增:支持前端菜单可以配置 * 功能新增:支持前端菜单可以配置
* 功能优化:在登录和注册界面标题显示软件版本号
* 功能优化MJ 绘画支持 --sref 和 --cref 图片一致性参数
* 功能优化:手机端支持免登录预览功能 * 功能优化:手机端支持免登录预览功能
* Bug修复解决因为图片上传使用相对路径而导致融图失败的问题。
* 功能新增:手机端支持 Stable-Diffusion 绘画 * 功能新增:手机端支持 Stable-Diffusion 绘画
* 功能新增:管理后台登录页面增加行为验证码,防止爆破 * 功能新增:管理后台登录页面增加行为验证码,防止爆破

View File

@ -91,7 +91,7 @@ KEY。
![](https://ai.r9it.com/docs/images/env/admin_api_keys.png) ![](https://ai.r9it.com/docs/images/env/admin_api_keys.png)
另外,如果您目前还没有 OpenAI 的 API KEY的推荐您去 https://gpt.bemore.lol 购买,**无需魔法,高速稳定,且价格还远低于 OpenAI 另外,如果您目前还没有 OpenAI 的 API KEY的推荐您去 https://api.chat-plus.net 购买,**无需魔法,高速稳定,且价格还远低于 OpenAI
官方**。 官方**。
## 使用须知 ## 使用须知

View File

@ -25,6 +25,7 @@ type MjTask struct {
Type TaskType `json:"type"` Type TaskType `json:"type"`
UserId int `json:"user_id"` UserId int `json:"user_id"`
Prompt string `json:"prompt,omitempty"` Prompt string `json:"prompt,omitempty"`
Params string `json:"full_prompt"`
Index int `json:"index,omitempty"` Index int `json:"index,omitempty"`
MessageId string `json:"message_id,omitempty"` MessageId string `json:"message_id,omitempty"`
MessageHash string `json:"message_hash,omitempty"` MessageHash string `json:"message_hash,omitempty"`

View File

@ -98,7 +98,10 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
ImgArr []string `json:"img_arr"` ImgArr []string `json:"img_arr"`
Tile bool `json:"tile"` Tile bool `json:"tile"`
Quality float32 `json:"quality"` Quality float32 `json:"quality"`
Weight float32 `json:"weight"` Iw float32 `json:"iw"`
CRef string `json:"cref"` //生成角色一致的图像
SRef string `json:"sref"` //生成风格一致的图像
Cw int `json:"cw"` // 参考程度
} }
if err := c.ShouldBindJSON(&data); err != nil { if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs) resp.ERROR(c, types.InvalidArgs)
@ -108,41 +111,53 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
return return
} }
var prompt = data.Prompt var params = ""
if data.Rate != "" && !strings.Contains(prompt, "--ar") { if data.Rate != "" && !strings.Contains(params, "--ar") {
prompt += " --ar " + data.Rate params += " --ar " + data.Rate
} }
if data.Seed > 0 && !strings.Contains(prompt, "--seed") { if data.Seed > 0 && !strings.Contains(params, "--seed") {
prompt += fmt.Sprintf(" --seed %d", data.Seed) params += fmt.Sprintf(" --seed %d", data.Seed)
} }
if data.Stylize > 0 && !strings.Contains(prompt, "--s") && !strings.Contains(prompt, "--stylize") { if data.Stylize > 0 && !strings.Contains(params, "--s") && !strings.Contains(params, "--stylize") {
prompt += fmt.Sprintf(" --s %d", data.Stylize) params += fmt.Sprintf(" --s %d", data.Stylize)
} }
if data.Chaos > 0 && !strings.Contains(prompt, "--c") && !strings.Contains(prompt, "--chaos") { if data.Chaos > 0 && !strings.Contains(params, "--c") && !strings.Contains(params, "--chaos") {
prompt += fmt.Sprintf(" --c %d", data.Chaos) params += fmt.Sprintf(" --c %d", data.Chaos)
} }
if data.Weight > 0 { if len(data.ImgArr) > 0 && data.Iw > 0 {
prompt += fmt.Sprintf(" --iw %f", data.Weight) params += fmt.Sprintf(" --iw %f", data.Iw)
} }
if data.Raw { if data.Raw {
prompt += " --style raw" params += " --style raw"
} }
if data.Quality > 0 { if data.Quality > 0 {
prompt += fmt.Sprintf(" --q %.2f", data.Quality) params += fmt.Sprintf(" --q %.2f", data.Quality)
} }
if data.NegPrompt != "" { if data.NegPrompt != "" {
prompt += fmt.Sprintf(" --no %s", data.NegPrompt) params += fmt.Sprintf(" --no %s", data.NegPrompt)
} }
if data.Tile { if data.Tile {
prompt += " --tile " params += " --tile "
} }
if data.Model != "" && !strings.Contains(prompt, "--v") && !strings.Contains(prompt, "--niji") { if data.CRef != "" {
prompt += fmt.Sprintf(" %s", data.Model) params += fmt.Sprintf(" --cref %s", data.CRef)
if data.Cw > 0 {
params += fmt.Sprintf(" --cw %d", data.Cw)
} else {
params += " --cw 100"
}
}
if data.SRef != "" {
params += fmt.Sprintf(" --sref %s", data.CRef)
}
if data.Model != "" && !strings.Contains(params, "--v") && !strings.Contains(params, "--niji") {
params += fmt.Sprintf(" %s", data.Model)
} }
// 处理融图和换脸的提示词 // 处理融图和换脸的提示词
if data.TaskType == types.TaskSwapFace.String() || data.TaskType == types.TaskBlend.String() { if data.TaskType == types.TaskSwapFace.String() || data.TaskType == types.TaskBlend.String() {
prompt = fmt.Sprintf("%s:%s", data.TaskType, strings.Join(data.ImgArr, ",")) params = fmt.Sprintf("%s:%s", data.TaskType, strings.Join(data.ImgArr, ","))
} }
// 如果本地图片上传的是相对地址,处理成绝对地址 // 如果本地图片上传的是相对地址,处理成绝对地址
@ -165,7 +180,7 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
UserId: userId, UserId: userId,
TaskId: taskId, TaskId: taskId,
Progress: 0, Progress: 0,
Prompt: prompt, Prompt: fmt.Sprintf("%s %s", data.Prompt, params),
Power: h.App.SysConfig.MjPower, Power: h.App.SysConfig.MjPower,
CreatedAt: time.Now(), CreatedAt: time.Now(),
} }
@ -188,7 +203,8 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
TaskId: taskId, TaskId: taskId,
SessionId: data.SessionId, SessionId: data.SessionId,
Type: types.TaskType(data.TaskType), Type: types.TaskType(data.TaskType),
Prompt: prompt, Prompt: data.Prompt,
Params: params,
UserId: userId, UserId: userId,
ImgArr: data.ImgArr, ImgArr: data.ImgArr,
}) })

View File

@ -53,6 +53,10 @@ func (l *AppLifecycle) OnStop(context.Context) error {
return nil return nil
} }
func NewAppLifeCycle() *AppLifecycle {
return &AppLifecycle{}
}
func main() { func main() {
configFile := os.Getenv("CONFIG_FILE") configFile := os.Getenv("CONFIG_FILE")
if configFile == "" { if configFile == "" {
@ -432,11 +436,14 @@ func main() {
group.GET("list", h.List) group.GET("list", h.List)
}), }),
fx.Invoke(func(s *core.AppServer, db *gorm.DB) { fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
err := s.Run(db) go func() {
if err != nil { err := s.Run(db)
log.Fatal(err) if err != nil {
} log.Fatal(err)
}
}()
}), }),
fx.Provide(NewAppLifeCycle),
// 注册生命周期回调函数 // 注册生命周期回调函数
fx.Invoke(func(lifecycle fx.Lifecycle, lc *AppLifecycle) { fx.Invoke(func(lifecycle fx.Lifecycle, lc *AppLifecycle) {
lifecycle.Append(fx.Hook{ lifecycle.Append(fx.Hook{

View File

@ -26,7 +26,7 @@ func (c *PlusClient) Imagine(task types.MjTask) (ImageRes, error) {
apiURL := fmt.Sprintf("%s/mj-%s/mj/submit/imagine", c.apiURL, c.Config.Mode) apiURL := fmt.Sprintf("%s/mj-%s/mj/submit/imagine", c.apiURL, c.Config.Mode)
body := ImageReq{ body := ImageReq{
BotType: "MID_JOURNEY", BotType: "MID_JOURNEY",
Prompt: task.Prompt, Prompt: fmt.Sprintf("%s %s", task.Prompt, task.Params),
Base64Array: make([]string, 0), Base64Array: make([]string, 0),
} }
// 生成图片 Base64 编码 // 生成图片 Base64 编码

View File

@ -23,7 +23,7 @@ func NewProxyClient(config types.MjProxyConfig) *ProxyClient {
func (c *ProxyClient) Imagine(task types.MjTask) (ImageRes, error) { func (c *ProxyClient) Imagine(task types.MjTask) (ImageRes, error) {
apiURL := fmt.Sprintf("%s/mj/submit/imagine", c.apiURL) apiURL := fmt.Sprintf("%s/mj/submit/imagine", c.apiURL)
body := ImageReq{ body := ImageReq{
Prompt: task.Prompt, Prompt: fmt.Sprintf("%s %s", task.Prompt, task.Params),
Base64Array: make([]string, 0), Base64Array: make([]string, 0),
} }
// 生成图片 Base64 编码 // 生成图片 Base64 编码
@ -46,8 +46,6 @@ func (c *ProxyClient) Imagine(task types.MjTask) (ImageRes, error) {
SetErrorResult(&errRes). SetErrorResult(&errRes).
Post(apiURL) Post(apiURL)
if err != nil { if err != nil {
all, err := io.ReadAll(r.Body)
logger.Info(string(all))
return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err) return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
} }

View File

@ -68,7 +68,7 @@ func (s *Service) Run() {
} }
// 如果是 mj-proxy 则自动翻译提示词 // 如果是 mj-proxy 则自动翻译提示词
if utils.HasChinese(task.Prompt) && strings.HasPrefix(s.Name, "mj-proxy-service") { if utils.HasChinese(task.Prompt) {
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt)) content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt))
if err == nil { if err == nil {
task.Prompt = content task.Prompt = content

View File

@ -168,7 +168,48 @@
<div class="extra-params"> <div class="extra-params">
<el-form> <el-form>
<el-tabs v-model="activeName" class="title-tabs" @tabChange="tabChange"> <el-tabs v-model="activeName" class="title-tabs" @tabChange="tabChange">
<el-tab-pane label="文生图(可选)" name="image"> <el-tab-pane label="文生图" name="txt2img">
<div class="prompt-box">
<div class="param-line pt">
<div class="flex-row justify-between items-center">
<div class="flex-row justify-start items-center">
<span>提示词</span>
<el-tooltip effect="light" content="输入你想要的内容,用逗号分割" placement="right">
<el-icon>
<InfoFilled/>
</el-icon>
</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="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
</div>
<div class="param-line pt">
<div class="flex-row justify-between items-center">
<div class="flex-row justify-start items-center">
<span>不希望出现的内容可选</span>
<el-tooltip effect="light" content="不想出现在图片上的元素(例如:树,建筑)" placement="right">
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</div>
</div>
<div class="param-line pt">
<el-input v-model="params.neg_prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
ref="promptRef"
placeholder="请在此输入你不希望出现在图片上的内容,系统会自动翻译中文提示词"/>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="图生图" name="img2img">
<div class="text">图生图以某张图片为底稿参考来创作绘画生成类似风格或类型图像支持 PNG JPG 格式图片 <div class="text">图生图以某张图片为底稿参考来创作绘画生成类似风格或类型图像支持 PNG JPG 格式图片
</div> </div>
<div class="param-line"> <div class="param-line">
@ -190,15 +231,15 @@
</div> </div>
<div class="param-line" style="padding-top: 10px"> <div class="param-line" style="padding-top: 10px">
<el-form-item label="图像权重:"> <el-form-item label="参考权重:">
<template #default> <template #default>
<div class="form-item-inner"> <div class="form-item-inner">
<el-slider v-model.number="params.weight" :max="1" :step="0.01" <el-slider v-model.number="params.iw" :max="1" :step="0.01"
style="width: 180px;--el-slider-main-bg-color:#47fff1"/> style="width: 180px;--el-slider-main-bg-color:#47fff1"/>
<el-tooltip effect="light" <el-tooltip effect="light"
content="使用图像权重参数--iw来调整图像 URL 与文本的重要性 <br/>权重较高时意味着图像提示将对完成的作业产生更大的影响" content="使用图像权重参数--iw来调整图像 URL 与文本的重要性 <br/>权重较高时意味着图像提示将对完成的作业产生更大的影响"
raw-content placement="right"> raw-content placement="right">
<el-icon style="margin-top: 9px"> <el-icon>
<InfoFilled/> <InfoFilled/>
</el-icon> </el-icon>
</el-tooltip> </el-tooltip>
@ -248,7 +289,7 @@
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="融图(可选)" name="blend"> <el-tab-pane label="融图" name="blend">
<div class="text">请上传两张以上的图片最多不超过五张超过五张图片请使用文生图功能</div> <div class="text">请上传两张以上的图片最多不超过五张超过五张图片请使用文生图功能</div>
<div class="img-inline"> <div class="img-inline">
<div class="img-list-box"> <div class="img-list-box">
@ -267,7 +308,7 @@
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="换脸(可选)" name="swapFace"> <el-tab-pane label="换脸" name="swapFace">
<div class="text">请上传两张有脸部的图片用右边图片的脸替换左边图片的脸</div> <div class="text">请上传两张有脸部的图片用右边图片的脸替换左边图片的脸</div>
<div class="img-inline"> <div class="img-inline">
<div class="img-list-box"> <div class="img-list-box">
@ -285,6 +326,115 @@
</el-upload> </el-upload>
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane name="cref">
<template #label>
<el-badge value="New">
<span>一致性</span>
</el-badge>
</template>
<div class="text">注意只有于 niji6 v6 模型支持一致性功能如果选择其他模型此功能将不起作用</div>
<div class="param-line">
<el-form-item label="角色一致性:" prop="cref">
<el-input v-model="params.cref" placeholder="请输入图片URL或者上传图片"
style="--el-input-focus-border-color:#47fff1;--el-input-text-color:#ffffff; max-width: 500px; width: 100%"
size="small">
<template #append>
<el-upload
:auto-upload="true"
:show-file-list="false"
@click="beforeUpload('cref')"
:http-request="uploadImg"
>
<el-icon class="uploader-icon">
<UploadFilled/>
</el-icon>
</el-upload>
</template>
</el-input>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="风格一致性:" prop="sref">
<el-input v-model="params.sref" placeholder="请输入图片URL或者上传图片"
style="--el-input-focus-border-color:#47fff1; --el-input-text-color:#ffffff; max-width: 500px; width: 100%"
size="small">
<template #append>
<el-upload
:auto-upload="true"
:show-file-list="false"
@click="beforeUpload('sref')"
:http-request="uploadImg"
>
<el-icon class="uploader-icon">
<UploadFilled/>
</el-icon>
</el-upload>
</template>
</el-input>
</el-form-item>
</div>
<div class="param-line" style="padding-top: 10px">
<el-form-item label="参考权重:">
<template #default>
<div class="form-item-inner">
<el-slider v-model.number="params.cw" :max="100" :step="1"
style="width: 180px;--el-slider-main-bg-color:#47fff1"/>
<el-tooltip effect="light"
content="取值范围 0-100 <br/>默认值100参考原图的脸部、头发和衣服<br/>0则表示只换脸"
raw-content placement="right">
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="prompt-box">
<div class="param-line pt">
<div class="flex-row justify-between items-center">
<div class="flex-row justify-start items-center">
<span>提示词</span>
<el-tooltip effect="light" content="输入你想要的内容,用逗号分割" placement="right">
<el-icon>
<InfoFilled/>
</el-icon>
</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="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
</div>
<div class="param-line pt">
<div class="flex-row justify-between items-center">
<div class="flex-row justify-start items-center">
<span>不希望出现的内容可选</span>
<el-tooltip effect="light" content="不想出现在图片上的元素(例如:树,建筑)" placement="right">
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</div>
</div>
<div class="param-line pt">
<el-input v-model="params.neg_prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
ref="promptRef"
placeholder="请在此输入你不希望出现在图片上的内容,系统会自动翻译中文提示词"/>
</div>
</div>
</el-tab-pane>
</el-tabs> </el-tabs>
<el-row class="text-info"> <el-row class="text-info">
@ -343,7 +493,7 @@
</div> </div>
<h2>创作记录</h2> <h2>创作记录</h2>
<div class="finish-job-list" v-loading="loading" element-loading-background="rgba(255, 255, 255, 0.5)"> <div class="finish-job-list" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.5)">
<div v-if="finishedJobs.length > 0"> <div v-if="finishedJobs.length > 0">
<ItemList :items="finishedJobs" :width="240" :gap="16"> <ItemList :items="finishedJobs" :width="240" :gap="16">
<template #default="scope"> <template #default="scope">
@ -508,7 +658,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} from "@element-plus/icons-vue"; import {ChromeFilled, Delete, DocumentCopy, InfoFilled, Picture, Plus, UploadFilled} 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";
@ -517,7 +667,7 @@ import Clipboard from "clipboard";
import {checkSession} from "@/action/session"; import {checkSession} from "@/action/session";
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import {getSessionId} from "@/store/session"; import {getSessionId} from "@/store/session";
import {removeArrayItem} from "@/utils/libs"; import {copyObj, removeArrayItem} from "@/utils/libs";
import LoginDialog from "@/components/LoginDialog.vue"; import LoginDialog from "@/components/LoginDialog.vue";
const listBoxHeight = ref(window.innerHeight - 40) const listBoxHeight = ref(window.innerHeight - 40)
@ -545,11 +695,12 @@ const models = [
{text: "优质模式MJ-5.1", value: " --v 5.1", img: "/images/mj/mj-v5.1.jpg"}, {text: "优质模式MJ-5.1", value: " --v 5.1", img: "/images/mj/mj-v5.1.jpg"},
{text: "虚幻模式MJ-5", value: " --v 5", img: "/images/mj/mj-v5.jpg"}, {text: "虚幻模式MJ-5", value: " --v 5", img: "/images/mj/mj-v5.jpg"},
{text: "真实模式MJ-4", value: " --v 4", img: "/images/mj/mj-v4.jpg"}, {text: "真实模式MJ-4", value: " --v 4", img: "/images/mj/mj-v4.jpg"},
{text: "动漫风niji5 原始", value: " --niji 5", img: "/images/mj/mj-niji.png"}, {text: "动漫风-niji4", value: " --niji 4", img: "/images/mj/nj4.jpg"},
{text: "动漫风niji5 可爱", value: " --niji 5 --style cute", img: "/images/mj/nj1.jpg"}, {text: "动漫风-niji5", value: " --niji 5", img: "/images/mj/mj-niji.png"},
{text: "动漫风niji5 风景", value: " --niji 5 --style scenic", img: "/images/mj/nj2.jpg"}, {text: "动漫风-niji5 可爱", value: " --niji 5 --style cute", img: "/images/mj/nj1.jpg"},
{text: "动漫风niji5 表现力", value: " --niji 5 --style expressive", img: "/images/mj/nj3.jpg"}, {text: "动漫风-niji5 风景", value: " --niji 5 --style scenic", img: "/images/mj/nj2.jpg"},
{text: "动漫风niji4", value: " --niji 4", img: "/images/mj/nj4.jpg"}, {text: "动漫风-niji6", value: " --niji 6", img: "/images/mj/nj3.jpg"},
] ]
const options = [ const options = [
@ -581,17 +732,20 @@ const initParams = {
seed: 0, seed: 0,
img_arr: [], img_arr: [],
raw: false, raw: false,
weight: 0.25, iw: 0,
prompt: router.currentRoute.value.params["prompt"] ?? "", prompt: router.currentRoute.value.params["prompt"] ?? "",
neg_prompt: "", neg_prompt: "",
tile: false, tile: false,
quality: 0 quality: 0,
cref: "",
sref: "",
cw: 0,
} }
const params = ref(initParams) const params = ref(copyObj(initParams))
const imgList = ref([]) const imgList = ref([])
const activeName = ref('image') const activeName = ref('txt2img')
const runningJobs = ref([]) const runningJobs = ref([])
const finishedJobs = ref([]) const finishedJobs = ref([])
@ -780,6 +934,11 @@ const changeModel = (item) => {
params.value.model = item.value params.value.model = item.value
} }
const imgKey = ref("")
const beforeUpload = (key) => {
imgKey.value = key
}
// //
const uploadImg = (file) => { const uploadImg = (file) => {
if (!isLogin.value) { if (!isLogin.value) {
@ -795,7 +954,12 @@ const uploadImg = (file) => {
formData.append('file', result, result.name); formData.append('file', result, result.name);
// //
httpPost('/api/upload', formData).then((res) => { httpPost('/api/upload', formData).then((res) => {
imgList.value.push(res.data.url) if (imgKey.value === '') {
imgList.value.push(res.data.url)
} else { //
params.value[imgKey.value] = res.data.url
imgKey.value = ''
}
ElMessage.success('上传成功') ElMessage.success('上传成功')
}).catch((e) => { }).catch((e) => {
ElMessage.error('上传失败:' + e.message) ElMessage.error('上传失败:' + e.message)
@ -830,7 +994,8 @@ const generate = () => {
httpPost("/api/mj/image", params.value).then(() => { httpPost("/api/mj/image", params.value).then(() => {
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...") ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
power.value -= mjPower.value power.value -= mjPower.value
params.value = initParams params.value = copyObj(initParams)
imgList.value = []
}).catch(e => { }).catch(e => {
ElMessage.error("任务推送失败:" + e.message) ElMessage.error("任务推送失败:" + e.message)
}) })
@ -897,7 +1062,11 @@ const publishImage = (item, action) => {
// //
const tabChange = (tab) => { const tabChange = (tab) => {
params.value.task_type = tab if (tab === "txt2img" || tab === "img2img" || tab === "cref") {
params.value.task_type = "image"
} else {
params.value.task_type = tab
}
} }
// //

View File

@ -222,7 +222,7 @@
</div> </div>
</div> </div>
<div class="param-line" v-loading="translating" element-loading-background="rgba(122, 122, 122, 0.8)"> <div class="param-line" v-loading="translating" element-loading-background="rgba(0, 0, 0, 0.5)">
<el-input <el-input
v-model="params.prompt" v-model="params.prompt"
:autosize="{ minRows: 4, maxRows: 6 }" :autosize="{ minRows: 4, maxRows: 6 }"