mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	feat: support midjourney --cref and --sref for role consistency
This commit is contained in:
		@@ -1,7 +1,10 @@
 | 
			
		||||
# 更新日志
 | 
			
		||||
## 4.0.2 
 | 
			
		||||
* 功能新增:支持前端菜单可以配置
 | 
			
		||||
* 功能优化:在登录和注册界面标题显示软件版本号
 | 
			
		||||
* 功能优化:MJ 绘画支持 --sref 和 --cref 图片一致性参数
 | 
			
		||||
* 功能优化:手机端支持免登录预览功能
 | 
			
		||||
* Bug修复:解决因为图片上传使用相对路径而导致融图失败的问题。
 | 
			
		||||
* 功能新增:手机端支持 Stable-Diffusion 绘画
 | 
			
		||||
* 功能新增:管理后台登录页面增加行为验证码,防止爆破
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,7 @@ KEY。
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
另外,如果您目前还没有 OpenAI 的 API KEY的,推荐您去 https://gpt.bemore.lol 购买,**无需魔法,高速稳定,且价格还远低于 OpenAI
 | 
			
		||||
另外,如果您目前还没有 OpenAI 的 API KEY的,推荐您去 https://api.chat-plus.net 购买,**无需魔法,高速稳定,且价格还远低于 OpenAI
 | 
			
		||||
官方**。
 | 
			
		||||
 | 
			
		||||
## 使用须知
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ type MjTask struct {
 | 
			
		||||
	Type        TaskType `json:"type"`
 | 
			
		||||
	UserId      int      `json:"user_id"`
 | 
			
		||||
	Prompt      string   `json:"prompt,omitempty"`
 | 
			
		||||
	Params      string   `json:"full_prompt"`
 | 
			
		||||
	Index       int      `json:"index,omitempty"`
 | 
			
		||||
	MessageId   string   `json:"message_id,omitempty"`
 | 
			
		||||
	MessageHash string   `json:"message_hash,omitempty"`
 | 
			
		||||
 
 | 
			
		||||
@@ -98,7 +98,10 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
 | 
			
		||||
		ImgArr    []string `json:"img_arr"`
 | 
			
		||||
		Tile      bool     `json:"tile"`
 | 
			
		||||
		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 {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
@@ -108,41 +111,53 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var prompt = data.Prompt
 | 
			
		||||
	if data.Rate != "" && !strings.Contains(prompt, "--ar") {
 | 
			
		||||
		prompt += " --ar " + data.Rate
 | 
			
		||||
	var params = ""
 | 
			
		||||
	if data.Rate != "" && !strings.Contains(params, "--ar") {
 | 
			
		||||
		params += " --ar " + data.Rate
 | 
			
		||||
	}
 | 
			
		||||
	if data.Seed > 0 && !strings.Contains(prompt, "--seed") {
 | 
			
		||||
		prompt += fmt.Sprintf(" --seed %d", data.Seed)
 | 
			
		||||
	if data.Seed > 0 && !strings.Contains(params, "--seed") {
 | 
			
		||||
		params += fmt.Sprintf(" --seed %d", data.Seed)
 | 
			
		||||
	}
 | 
			
		||||
	if data.Stylize > 0 && !strings.Contains(prompt, "--s") && !strings.Contains(prompt, "--stylize") {
 | 
			
		||||
		prompt += fmt.Sprintf(" --s %d", data.Stylize)
 | 
			
		||||
	if data.Stylize > 0 && !strings.Contains(params, "--s") && !strings.Contains(params, "--stylize") {
 | 
			
		||||
		params += fmt.Sprintf(" --s %d", data.Stylize)
 | 
			
		||||
	}
 | 
			
		||||
	if data.Chaos > 0 && !strings.Contains(prompt, "--c") && !strings.Contains(prompt, "--chaos") {
 | 
			
		||||
		prompt += fmt.Sprintf(" --c %d", data.Chaos)
 | 
			
		||||
	if data.Chaos > 0 && !strings.Contains(params, "--c") && !strings.Contains(params, "--chaos") {
 | 
			
		||||
		params += fmt.Sprintf(" --c %d", data.Chaos)
 | 
			
		||||
	}
 | 
			
		||||
	if data.Weight > 0 {
 | 
			
		||||
		prompt += fmt.Sprintf(" --iw %f", data.Weight)
 | 
			
		||||
	if len(data.ImgArr) > 0 && data.Iw > 0 {
 | 
			
		||||
		params += fmt.Sprintf(" --iw %f", data.Iw)
 | 
			
		||||
	}
 | 
			
		||||
	if data.Raw {
 | 
			
		||||
		prompt += " --style raw"
 | 
			
		||||
		params += " --style raw"
 | 
			
		||||
	}
 | 
			
		||||
	if data.Quality > 0 {
 | 
			
		||||
		prompt += fmt.Sprintf(" --q %.2f", data.Quality)
 | 
			
		||||
		params += fmt.Sprintf(" --q %.2f", data.Quality)
 | 
			
		||||
	}
 | 
			
		||||
	if data.NegPrompt != "" {
 | 
			
		||||
		prompt += fmt.Sprintf(" --no %s", data.NegPrompt)
 | 
			
		||||
		params += fmt.Sprintf(" --no %s", data.NegPrompt)
 | 
			
		||||
	}
 | 
			
		||||
	if data.Tile {
 | 
			
		||||
		prompt += " --tile "
 | 
			
		||||
		params += " --tile "
 | 
			
		||||
	}
 | 
			
		||||
	if data.Model != "" && !strings.Contains(prompt, "--v") && !strings.Contains(prompt, "--niji") {
 | 
			
		||||
		prompt += fmt.Sprintf(" %s", data.Model)
 | 
			
		||||
	if data.CRef != "" {
 | 
			
		||||
		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() {
 | 
			
		||||
		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,
 | 
			
		||||
		TaskId:    taskId,
 | 
			
		||||
		Progress:  0,
 | 
			
		||||
		Prompt:    prompt,
 | 
			
		||||
		Prompt:    fmt.Sprintf("%s %s", data.Prompt, params),
 | 
			
		||||
		Power:     h.App.SysConfig.MjPower,
 | 
			
		||||
		CreatedAt: time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
@@ -188,7 +203,8 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
 | 
			
		||||
		TaskId:    taskId,
 | 
			
		||||
		SessionId: data.SessionId,
 | 
			
		||||
		Type:      types.TaskType(data.TaskType),
 | 
			
		||||
		Prompt:    prompt,
 | 
			
		||||
		Prompt:    data.Prompt,
 | 
			
		||||
		Params:    params,
 | 
			
		||||
		UserId:    userId,
 | 
			
		||||
		ImgArr:    data.ImgArr,
 | 
			
		||||
	})
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								api/main.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								api/main.go
									
									
									
									
									
								
							@@ -53,6 +53,10 @@ func (l *AppLifecycle) OnStop(context.Context) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAppLifeCycle() *AppLifecycle {
 | 
			
		||||
	return &AppLifecycle{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	configFile := os.Getenv("CONFIG_FILE")
 | 
			
		||||
	if configFile == "" {
 | 
			
		||||
@@ -432,11 +436,14 @@ func main() {
 | 
			
		||||
			group.GET("list", h.List)
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
 | 
			
		||||
			err := s.Run(db)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
			go func() {
 | 
			
		||||
				err := s.Run(db)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Provide(NewAppLifeCycle),
 | 
			
		||||
		// 注册生命周期回调函数
 | 
			
		||||
		fx.Invoke(func(lifecycle fx.Lifecycle, lc *AppLifecycle) {
 | 
			
		||||
			lifecycle.Append(fx.Hook{
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
	body := ImageReq{
 | 
			
		||||
		BotType:     "MID_JOURNEY",
 | 
			
		||||
		Prompt:      task.Prompt,
 | 
			
		||||
		Prompt:      fmt.Sprintf("%s %s", task.Prompt, task.Params),
 | 
			
		||||
		Base64Array: make([]string, 0),
 | 
			
		||||
	}
 | 
			
		||||
	// 生成图片 Base64 编码
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ func NewProxyClient(config types.MjProxyConfig) *ProxyClient {
 | 
			
		||||
func (c *ProxyClient) Imagine(task types.MjTask) (ImageRes, error) {
 | 
			
		||||
	apiURL := fmt.Sprintf("%s/mj/submit/imagine", c.apiURL)
 | 
			
		||||
	body := ImageReq{
 | 
			
		||||
		Prompt:      task.Prompt,
 | 
			
		||||
		Prompt:      fmt.Sprintf("%s %s", task.Prompt, task.Params),
 | 
			
		||||
		Base64Array: make([]string, 0),
 | 
			
		||||
	}
 | 
			
		||||
	// 生成图片 Base64 编码
 | 
			
		||||
@@ -46,8 +46,6 @@ func (c *ProxyClient) Imagine(task types.MjTask) (ImageRes, error) {
 | 
			
		||||
		SetErrorResult(&errRes).
 | 
			
		||||
		Post(apiURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		all, err := io.ReadAll(r.Body)
 | 
			
		||||
		logger.Info(string(all))
 | 
			
		||||
		return ImageRes{}, fmt.Errorf("请求 API %s 出错:%v", apiURL, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,7 @@ func (s *Service) Run() {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 如果是 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))
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				task.Prompt = content
 | 
			
		||||
 
 | 
			
		||||
@@ -168,7 +168,48 @@
 | 
			
		||||
          <div class="extra-params">
 | 
			
		||||
            <el-form>
 | 
			
		||||
              <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>
 | 
			
		||||
                  <div class="param-line">
 | 
			
		||||
@@ -190,15 +231,15 @@
 | 
			
		||||
                  </div>
 | 
			
		||||
 | 
			
		||||
                  <div class="param-line" style="padding-top: 10px">
 | 
			
		||||
                    <el-form-item label="图像权重:">
 | 
			
		||||
                    <el-form-item label="参考权重:">
 | 
			
		||||
                      <template #default>
 | 
			
		||||
                        <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"/>
 | 
			
		||||
                          <el-tooltip effect="light"
 | 
			
		||||
                                      content="使用图像权重参数--iw来调整图像 URL 与文本的重要性 <br/>权重较高时意味着图像提示将对完成的作业产生更大的影响"
 | 
			
		||||
                                      raw-content placement="right">
 | 
			
		||||
                            <el-icon style="margin-top: 9px">
 | 
			
		||||
                            <el-icon>
 | 
			
		||||
                              <InfoFilled/>
 | 
			
		||||
                            </el-icon>
 | 
			
		||||
                          </el-tooltip>
 | 
			
		||||
@@ -248,7 +289,7 @@
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-tab-pane>
 | 
			
		||||
 | 
			
		||||
                <el-tab-pane label="融图(可选)" name="blend">
 | 
			
		||||
                <el-tab-pane label="融图" name="blend">
 | 
			
		||||
                  <div class="text">请上传两张以上的图片,最多不超过五张,超过五张图片请使用文生图功能</div>
 | 
			
		||||
                  <div class="img-inline">
 | 
			
		||||
                    <div class="img-list-box">
 | 
			
		||||
@@ -267,7 +308,7 @@
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-tab-pane>
 | 
			
		||||
 | 
			
		||||
                <el-tab-pane label="换脸(可选)" name="swapFace">
 | 
			
		||||
                <el-tab-pane label="换脸" name="swapFace">
 | 
			
		||||
                  <div class="text">请上传两张有脸部的图片,用右边图片的脸替换左边图片的脸</div>
 | 
			
		||||
                  <div class="img-inline">
 | 
			
		||||
                    <div class="img-list-box">
 | 
			
		||||
@@ -285,6 +326,115 @@
 | 
			
		||||
                    </el-upload>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </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-row class="text-info">
 | 
			
		||||
@@ -343,7 +493,7 @@
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <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">
 | 
			
		||||
                <ItemList :items="finishedJobs" :width="240" :gap="16">
 | 
			
		||||
                  <template #default="scope">
 | 
			
		||||
@@ -508,7 +658,7 @@
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
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 {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
 | 
			
		||||
@@ -517,7 +667,7 @@ import Clipboard from "clipboard";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import {getSessionId} from "@/store/session";
 | 
			
		||||
import {removeArrayItem} from "@/utils/libs";
 | 
			
		||||
import {copyObj, removeArrayItem} from "@/utils/libs";
 | 
			
		||||
import LoginDialog from "@/components/LoginDialog.vue";
 | 
			
		||||
 | 
			
		||||
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", value: " --v 5", img: "/images/mj/mj-v5.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: "动漫风niji5 可爱", value: " --niji 5 --style cute", img: "/images/mj/nj1.jpg"},
 | 
			
		||||
  {text: "动漫风niji5 风景", value: " --niji 5 --style scenic", img: "/images/mj/nj2.jpg"},
 | 
			
		||||
  {text: "动漫风niji5 表现力", value: " --niji 5 --style expressive", img: "/images/mj/nj3.jpg"},
 | 
			
		||||
  {text: "动漫风niji4", value: " --niji 4", img: "/images/mj/nj4.jpg"},
 | 
			
		||||
  {text: "动漫风-niji4", value: " --niji 4", img: "/images/mj/nj4.jpg"},
 | 
			
		||||
  {text: "动漫风-niji5", value: " --niji 5", img: "/images/mj/mj-niji.png"},
 | 
			
		||||
  {text: "动漫风-niji5 可爱", value: " --niji 5 --style cute", img: "/images/mj/nj1.jpg"},
 | 
			
		||||
  {text: "动漫风-niji5 风景", value: " --niji 5 --style scenic", img: "/images/mj/nj2.jpg"},
 | 
			
		||||
  {text: "动漫风-niji6", value: " --niji 6", img: "/images/mj/nj3.jpg"},
 | 
			
		||||
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
const options = [
 | 
			
		||||
@@ -581,17 +732,20 @@ const initParams = {
 | 
			
		||||
  seed: 0,
 | 
			
		||||
  img_arr: [],
 | 
			
		||||
  raw: false,
 | 
			
		||||
  weight: 0.25,
 | 
			
		||||
  iw: 0,
 | 
			
		||||
  prompt: router.currentRoute.value.params["prompt"] ?? "",
 | 
			
		||||
  neg_prompt: "",
 | 
			
		||||
  tile: false,
 | 
			
		||||
  quality: 0
 | 
			
		||||
  quality: 0,
 | 
			
		||||
  cref: "",
 | 
			
		||||
  sref: "",
 | 
			
		||||
  cw: 0,
 | 
			
		||||
}
 | 
			
		||||
const params = ref(initParams)
 | 
			
		||||
const params = ref(copyObj(initParams))
 | 
			
		||||
 | 
			
		||||
const imgList = ref([])
 | 
			
		||||
 | 
			
		||||
const activeName = ref('image')
 | 
			
		||||
const activeName = ref('txt2img')
 | 
			
		||||
 | 
			
		||||
const runningJobs = ref([])
 | 
			
		||||
const finishedJobs = ref([])
 | 
			
		||||
@@ -780,6 +934,11 @@ const changeModel = (item) => {
 | 
			
		||||
  params.value.model = item.value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const imgKey = ref("")
 | 
			
		||||
const beforeUpload = (key) => {
 | 
			
		||||
  imgKey.value = key
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 图片上传
 | 
			
		||||
const uploadImg = (file) => {
 | 
			
		||||
  if (!isLogin.value) {
 | 
			
		||||
@@ -795,7 +954,12 @@ const uploadImg = (file) => {
 | 
			
		||||
      formData.append('file', result, result.name);
 | 
			
		||||
      // 执行上传操作
 | 
			
		||||
      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('上传成功')
 | 
			
		||||
      }).catch((e) => {
 | 
			
		||||
        ElMessage.error('上传失败:' + e.message)
 | 
			
		||||
@@ -830,7 +994,8 @@ const generate = () => {
 | 
			
		||||
  httpPost("/api/mj/image", params.value).then(() => {
 | 
			
		||||
    ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
 | 
			
		||||
    power.value -= mjPower.value
 | 
			
		||||
    params.value = initParams
 | 
			
		||||
    params.value = copyObj(initParams)
 | 
			
		||||
    imgList.value = []
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    ElMessage.error("任务推送失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
@@ -897,7 +1062,11 @@ const publishImage = (item, action) => {
 | 
			
		||||
 | 
			
		||||
// 切换菜单
 | 
			
		||||
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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 删除已上传图片
 | 
			
		||||
 
 | 
			
		||||
@@ -222,7 +222,7 @@
 | 
			
		||||
                </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
 | 
			
		||||
                    v-model="params.prompt"
 | 
			
		||||
                    :autosize="{ minRows: 4, maxRows: 6 }"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user