mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	tidy apis
This commit is contained in:
		@@ -32,31 +32,19 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AppServer struct {
 | 
			
		||||
	Debug        bool
 | 
			
		||||
	Config       *types.AppConfig
 | 
			
		||||
	Engine       *gin.Engine
 | 
			
		||||
	ChatContexts *types.LMap[string, []types.Message] // 聊天上下文 Map [chatId] => []Message
 | 
			
		||||
 | 
			
		||||
	Debug     bool
 | 
			
		||||
	Config    *types.AppConfig
 | 
			
		||||
	Engine    *gin.Engine
 | 
			
		||||
	SysConfig *types.SystemConfig // system config cache
 | 
			
		||||
 | 
			
		||||
	// 保存 Websocket 会话 UserId, 每个 UserId 只能连接一次
 | 
			
		||||
	// 防止第三方直接连接 socket 调用 OpenAI API
 | 
			
		||||
	ChatSession   *types.LMap[string, *types.ChatSession] //map[sessionId]UserId
 | 
			
		||||
	ChatClients   *types.LMap[string, *types.WsClient]    // map[sessionId]Websocket 连接集合
 | 
			
		||||
	ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewServer(appConfig *types.AppConfig) *AppServer {
 | 
			
		||||
	gin.SetMode(gin.ReleaseMode)
 | 
			
		||||
	gin.DefaultWriter = io.Discard
 | 
			
		||||
	return &AppServer{
 | 
			
		||||
		Debug:         false,
 | 
			
		||||
		Config:        appConfig,
 | 
			
		||||
		Engine:        gin.Default(),
 | 
			
		||||
		ChatContexts:  types.NewLMap[string, []types.Message](),
 | 
			
		||||
		ChatSession:   types.NewLMap[string, *types.ChatSession](),
 | 
			
		||||
		ChatClients:   types.NewLMap[string, *types.WsClient](),
 | 
			
		||||
		ReqCancelFunc: types.NewLMap[string, context.CancelFunc](),
 | 
			
		||||
		Debug:  false,
 | 
			
		||||
		Config: appConfig,
 | 
			
		||||
		Engine: gin.Default(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -53,9 +53,8 @@ type Delta struct {
 | 
			
		||||
// ChatSession 聊天会话对象
 | 
			
		||||
type ChatSession struct {
 | 
			
		||||
	SessionId string    `json:"session_id"`
 | 
			
		||||
	UserId    uint      `json:"user_id"`
 | 
			
		||||
	ClientIP  string    `json:"client_ip"` // 客户端 IP
 | 
			
		||||
	Username  string    `json:"username"`  // 当前登录的 username
 | 
			
		||||
	UserId    uint      `json:"user_id"`   // 当前登录的 user ID
 | 
			
		||||
	ChatId    string    `json:"chat_id"`   // 客户端聊天会话 ID, 多会话模式专用字段
 | 
			
		||||
	Model     ChatModel `json:"model"`     // GPT 模型
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,10 +53,10 @@ type SdTaskParams struct {
 | 
			
		||||
	NegPrompt    string  `json:"neg_prompt"` // 反向提示词
 | 
			
		||||
	Steps        int     `json:"steps"`      // 迭代步数,默认20
 | 
			
		||||
	Sampler      string  `json:"sampler"`    // 采样器
 | 
			
		||||
	Scheduler    string  `json:"scheduler"`
 | 
			
		||||
	FaceFix      bool    `json:"face_fix"`  // 面部修复
 | 
			
		||||
	CfgScale     float32 `json:"cfg_scale"` //引导系数,默认 7
 | 
			
		||||
	Seed         int64   `json:"seed"`      // 随机数种子
 | 
			
		||||
	Scheduler    string  `json:"scheduler"`  // 采样调度
 | 
			
		||||
	FaceFix      bool    `json:"face_fix"`   // 面部修复
 | 
			
		||||
	CfgScale     float32 `json:"cfg_scale"`  //引导系数,默认 7
 | 
			
		||||
	Seed         int64   `json:"seed"`       // 随机数种子
 | 
			
		||||
	Height       int     `json:"height"`
 | 
			
		||||
	Width        int     `json:"width"`
 | 
			
		||||
	HdFix        bool    `json:"hd_fix"`         // 启用高清修复
 | 
			
		||||
 
 | 
			
		||||
@@ -37,11 +37,9 @@ type BizCode int
 | 
			
		||||
const (
 | 
			
		||||
	Success       = BizCode(0)
 | 
			
		||||
	Failed        = BizCode(1)
 | 
			
		||||
	NotAuthorized = BizCode(400) // 未授权
 | 
			
		||||
	NotPermission = BizCode(403) // 没有权限
 | 
			
		||||
	NotAuthorized = BizCode(401) // 未授权
 | 
			
		||||
 | 
			
		||||
	OkMsg       = "Success"
 | 
			
		||||
	ErrorMsg    = "系统开小差了"
 | 
			
		||||
	InvalidArgs = "非法参数或参数解析失败"
 | 
			
		||||
	NoData      = "No Data"
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,8 @@ type ChatHandler struct {
 | 
			
		||||
	redis          *redis.Client
 | 
			
		||||
	uploadManager  *oss.UploaderManager
 | 
			
		||||
	licenseService *service.LicenseService
 | 
			
		||||
	ReqCancelFunc  *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function
 | 
			
		||||
	ChatContexts   *types.LMap[string, []types.Message]    // 聊天上下文 Map [chatId] => []Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manager *oss.UploaderManager, licenseService *service.LicenseService) *ChatHandler {
 | 
			
		||||
@@ -52,6 +54,8 @@ func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manag
 | 
			
		||||
		redis:          redis,
 | 
			
		||||
		uploadManager:  manager,
 | 
			
		||||
		licenseService: licenseService,
 | 
			
		||||
		ReqCancelFunc:  types.NewLMap[string, context.CancelFunc](),
 | 
			
		||||
		ChatContexts:   types.NewLMap[string, []types.Message](),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -89,21 +93,10 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	session := h.App.ChatSession.Get(sessionId)
 | 
			
		||||
	if session == nil {
 | 
			
		||||
		user, err := h.GetLoginUser(c)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Info("用户未登录")
 | 
			
		||||
			c.Abort()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		session = &types.ChatSession{
 | 
			
		||||
			SessionId: sessionId,
 | 
			
		||||
			ClientIP:  c.ClientIP(),
 | 
			
		||||
			Username:  user.Username,
 | 
			
		||||
			UserId:    user.Id,
 | 
			
		||||
		}
 | 
			
		||||
		h.App.ChatSession.Put(sessionId, session)
 | 
			
		||||
	session := &types.ChatSession{
 | 
			
		||||
		SessionId: sessionId,
 | 
			
		||||
		ClientIP:  c.ClientIP(),
 | 
			
		||||
		UserId:    h.GetLoginUserId(c),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// use old chat data override the chat model and role ID
 | 
			
		||||
@@ -125,22 +118,18 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) {
 | 
			
		||||
		Temperature: chatModel.Temperature,
 | 
			
		||||
		KeyId:       chatModel.KeyId,
 | 
			
		||||
		Platform:    chatModel.Platform}
 | 
			
		||||
	logger.Infof("New websocket connected, IP: %s, Username: %s", c.ClientIP(), session.Username)
 | 
			
		||||
	logger.Infof("New websocket connected, IP: %s", c.ClientIP())
 | 
			
		||||
 | 
			
		||||
	// 保存会话连接
 | 
			
		||||
	h.App.ChatClients.Put(sessionId, client)
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			_, msg, err := client.Receive()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logger.Debugf("close connection: %s", client.Conn.RemoteAddr())
 | 
			
		||||
				client.Close()
 | 
			
		||||
				h.App.ChatClients.Delete(sessionId)
 | 
			
		||||
				h.App.ChatSession.Delete(sessionId)
 | 
			
		||||
				cancelFunc := h.App.ReqCancelFunc.Get(sessionId)
 | 
			
		||||
				cancelFunc := h.ReqCancelFunc.Get(sessionId)
 | 
			
		||||
				if cancelFunc != nil {
 | 
			
		||||
					cancelFunc()
 | 
			
		||||
					h.App.ReqCancelFunc.Delete(sessionId)
 | 
			
		||||
					h.ReqCancelFunc.Delete(sessionId)
 | 
			
		||||
				}
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
@@ -160,7 +149,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) {
 | 
			
		||||
			logger.Info("Receive a message: ", message.Content)
 | 
			
		||||
 | 
			
		||||
			ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
			h.App.ReqCancelFunc.Put(sessionId, cancel)
 | 
			
		||||
			h.ReqCancelFunc.Put(sessionId, cancel)
 | 
			
		||||
			// 回复消息
 | 
			
		||||
			err = h.sendMessage(ctx, session, chatRole, utils.InterfaceToString(message.Content), client)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
@@ -274,8 +263,8 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio
 | 
			
		||||
	chatCtx := make([]types.Message, 0)
 | 
			
		||||
	messages := make([]types.Message, 0)
 | 
			
		||||
	if h.App.SysConfig.EnableContext {
 | 
			
		||||
		if h.App.ChatContexts.Has(session.ChatId) {
 | 
			
		||||
			messages = h.App.ChatContexts.Get(session.ChatId)
 | 
			
		||||
		if h.ChatContexts.Has(session.ChatId) {
 | 
			
		||||
			messages = h.ChatContexts.Get(session.ChatId)
 | 
			
		||||
		} else {
 | 
			
		||||
			_ = utils.JsonDecode(role.Context, &messages)
 | 
			
		||||
			if h.App.SysConfig.ContextDeep > 0 {
 | 
			
		||||
@@ -468,9 +457,9 @@ func getTotalTokens(req types.ApiRequest) int {
 | 
			
		||||
// StopGenerate 停止生成
 | 
			
		||||
func (h *ChatHandler) StopGenerate(c *gin.Context) {
 | 
			
		||||
	sessionId := c.Query("session_id")
 | 
			
		||||
	if h.App.ReqCancelFunc.Has(sessionId) {
 | 
			
		||||
		h.App.ReqCancelFunc.Get(sessionId)()
 | 
			
		||||
		h.App.ReqCancelFunc.Delete(sessionId)
 | 
			
		||||
	if h.ReqCancelFunc.Has(sessionId) {
 | 
			
		||||
		h.ReqCancelFunc.Get(sessionId)()
 | 
			
		||||
		h.ReqCancelFunc.Delete(sessionId)
 | 
			
		||||
	}
 | 
			
		||||
	resp.SUCCESS(c, types.OkMsg)
 | 
			
		||||
}
 | 
			
		||||
@@ -628,7 +617,7 @@ func (h *ChatHandler) saveChatHistory(
 | 
			
		||||
	if h.App.SysConfig.EnableContext {
 | 
			
		||||
		chatCtx = append(chatCtx, useMsg)  // 提问消息
 | 
			
		||||
		chatCtx = append(chatCtx, message) // 回复消息
 | 
			
		||||
		h.App.ChatContexts.Put(session.ChatId, chatCtx)
 | 
			
		||||
		h.ChatContexts.Put(session.ChatId, chatCtx)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 追加聊天记录
 | 
			
		||||
@@ -686,7 +675,7 @@ func (h *ChatHandler) saveChatHistory(
 | 
			
		||||
	res = h.DB.Where("chat_id = ?", session.ChatId).First(&chatItem)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		chatItem.ChatId = session.ChatId
 | 
			
		||||
		chatItem.UserId = session.UserId
 | 
			
		||||
		chatItem.UserId = userVo.Id
 | 
			
		||||
		chatItem.RoleId = role.Id
 | 
			
		||||
		chatItem.ModelId = session.Model.Id
 | 
			
		||||
		if utf8.RuneCountInString(prompt) > 30 {
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,7 @@ func (h *ChatHandler) Clear(c *gin.Context) {
 | 
			
		||||
	for _, chat := range chats {
 | 
			
		||||
		chatIds = append(chatIds, chat.ChatId)
 | 
			
		||||
		// 清空会话上下文
 | 
			
		||||
		h.App.ChatContexts.Delete(chat.ChatId)
 | 
			
		||||
		h.ChatContexts.Delete(chat.ChatId)
 | 
			
		||||
	}
 | 
			
		||||
	err = h.DB.Transaction(func(tx *gorm.DB) error {
 | 
			
		||||
		res := h.DB.Where("user_id =?", user.Id).Delete(&model.ChatItem{})
 | 
			
		||||
@@ -108,8 +108,6 @@ func (h *ChatHandler) Clear(c *gin.Context) {
 | 
			
		||||
		if res.Error != nil {
 | 
			
		||||
			return res.Error
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: 是否要删除 MidJourney 绘画记录和图片文件?
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
@@ -175,7 +173,7 @@ func (h *ChatHandler) Remove(c *gin.Context) {
 | 
			
		||||
	// TODO: 是否要删除 MidJourney 绘画记录和图片文件?
 | 
			
		||||
 | 
			
		||||
	// 清空会话上下文
 | 
			
		||||
	h.App.ChatContexts.Delete(chatId)
 | 
			
		||||
	h.ChatContexts.Delete(chatId)
 | 
			
		||||
	resp.SUCCESS(c, types.OkMsg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -158,13 +158,13 @@ func (h *DallJobHandler) ImgWall(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
// JobList 获取 SD 任务列表
 | 
			
		||||
func (h *DallJobHandler) JobList(c *gin.Context) {
 | 
			
		||||
	status := h.GetBool(c, "status")
 | 
			
		||||
	finish := h.GetBool(c, "finish")
 | 
			
		||||
	userId := h.GetLoginUserId(c)
 | 
			
		||||
	page := h.GetInt(c, "page", 0)
 | 
			
		||||
	pageSize := h.GetInt(c, "page_size", 0)
 | 
			
		||||
	publish := h.GetBool(c, "publish")
 | 
			
		||||
 | 
			
		||||
	err, jobs := h.getData(status, userId, page, pageSize, publish)
 | 
			
		||||
	err, jobs := h.getData(finish, userId, page, pageSize, publish)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
@@ -214,25 +214,23 @@ func (h *DallJobHandler) getData(finish bool, userId uint, page int, pageSize in
 | 
			
		||||
 | 
			
		||||
// Remove remove task image
 | 
			
		||||
func (h *DallJobHandler) Remove(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Id     uint   `json:"id"`
 | 
			
		||||
		UserId uint   `json:"user_id"`
 | 
			
		||||
		ImgURL string `json:"img_url"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
	id := h.GetInt(c, "id", 0)
 | 
			
		||||
	userId := h.GetInt(c, "user_id", 0)
 | 
			
		||||
	var job model.DallJob
 | 
			
		||||
	if res := h.DB.Where("id = ? AND user_id = ?", id, userId).First(&job); res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "记录不存在")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove job recode
 | 
			
		||||
	res := h.DB.Delete(&model.DallJob{Id: data.Id})
 | 
			
		||||
	res := h.DB.Delete(&model.DallJob{Id: job.Id})
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, res.Error.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove image
 | 
			
		||||
	err := h.uploader.GetUploadHandler().Delete(data.ImgURL)
 | 
			
		||||
	err := h.uploader.GetUploadHandler().Delete(job.ImgURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Error("remove image failed: ", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -242,16 +240,11 @@ func (h *DallJobHandler) Remove(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
// Publish 发布/取消发布图片到画廊显示
 | 
			
		||||
func (h *DallJobHandler) Publish(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Id     uint `json:"id"`
 | 
			
		||||
		Action bool `json:"action"` // 发布动作,true => 发布,false => 取消分享
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	id := h.GetInt(c, "id", 0)
 | 
			
		||||
	userId := h.GetInt(c, "user_id", 0)
 | 
			
		||||
	action := h.GetBool(c, "action") // 发布动作,true => 发布,false => 取消分享
 | 
			
		||||
 | 
			
		||||
	res := h.DB.Model(&model.DallJob{Id: data.Id}).UpdateColumn("publish", true)
 | 
			
		||||
	res := h.DB.Model(&model.DallJob{Id: uint(id), UserId: uint(userId)}).UpdateColumn("publish", action)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		logger.Error("error with update database:", res.Error)
 | 
			
		||||
		resp.ERROR(c, "更新数据库失败")
 | 
			
		||||
 
 | 
			
		||||
@@ -92,19 +92,18 @@ func (h *MidJourneyHandler) Client(c *gin.Context) {
 | 
			
		||||
// Image 创建一个绘画任务
 | 
			
		||||
func (h *MidJourneyHandler) Image(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		SessionId string   `json:"session_id"`
 | 
			
		||||
		TaskType  string   `json:"task_type"`
 | 
			
		||||
		Prompt    string   `json:"prompt"`
 | 
			
		||||
		NegPrompt string   `json:"neg_prompt"`
 | 
			
		||||
		Rate      string   `json:"rate"`
 | 
			
		||||
		Model     string   `json:"model"`
 | 
			
		||||
		Chaos     int      `json:"chaos"`
 | 
			
		||||
		Raw       bool     `json:"raw"`
 | 
			
		||||
		Seed      int64    `json:"seed"`
 | 
			
		||||
		Stylize   int      `json:"stylize"`
 | 
			
		||||
		Model     string   `json:"model"`   // 模型
 | 
			
		||||
		Chaos     int      `json:"chaos"`   // 创意度取值范围: 0-100
 | 
			
		||||
		Raw       bool     `json:"raw"`     // 是否开启原始模型
 | 
			
		||||
		Seed      int64    `json:"seed"`    // 随机数
 | 
			
		||||
		Stylize   int      `json:"stylize"` // 风格化
 | 
			
		||||
		ImgArr    []string `json:"img_arr"`
 | 
			
		||||
		Tile      bool     `json:"tile"`
 | 
			
		||||
		Quality   float32  `json:"quality"`
 | 
			
		||||
		Tile      bool     `json:"tile"`    // 重复平铺
 | 
			
		||||
		Quality   float32  `json:"quality"` // 画质
 | 
			
		||||
		Iw        float32  `json:"iw"`
 | 
			
		||||
		CRef      string   `json:"cref"` //生成角色一致的图像
 | 
			
		||||
		SRef      string   `json:"sref"` //生成风格一致的图像
 | 
			
		||||
@@ -243,17 +242,12 @@ type reqVo struct {
 | 
			
		||||
	ChannelId   string `json:"channel_id"`
 | 
			
		||||
	MessageId   string `json:"message_id"`
 | 
			
		||||
	MessageHash string `json:"message_hash"`
 | 
			
		||||
	SessionId   string `json:"session_id"`
 | 
			
		||||
	Prompt      string `json:"prompt"`
 | 
			
		||||
	ChatId      string `json:"chat_id"`
 | 
			
		||||
	RoleId      int    `json:"role_id"`
 | 
			
		||||
	Icon        string `json:"icon"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Upscale send upscale command to MidJourney Bot
 | 
			
		||||
func (h *MidJourneyHandler) Upscale(c *gin.Context) {
 | 
			
		||||
	var data reqVo
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil || data.SessionId == "" {
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -271,7 +265,6 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
 | 
			
		||||
		UserId:      userId,
 | 
			
		||||
		TaskId:      taskId,
 | 
			
		||||
		Progress:    0,
 | 
			
		||||
		Prompt:      data.Prompt,
 | 
			
		||||
		Power:       h.App.SysConfig.MjActionPower,
 | 
			
		||||
		CreatedAt:   time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
@@ -283,7 +276,6 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
 | 
			
		||||
	h.pool.PushTask(types.MjTask{
 | 
			
		||||
		Id:          job.Id,
 | 
			
		||||
		Type:        types.TaskUpscale,
 | 
			
		||||
		Prompt:      data.Prompt,
 | 
			
		||||
		UserId:      userId,
 | 
			
		||||
		ChannelId:   data.ChannelId,
 | 
			
		||||
		Index:       data.Index,
 | 
			
		||||
@@ -318,7 +310,7 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
 | 
			
		||||
// Variation send variation command to MidJourney Bot
 | 
			
		||||
func (h *MidJourneyHandler) Variation(c *gin.Context) {
 | 
			
		||||
	var data reqVo
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil || data.SessionId == "" {
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -337,7 +329,6 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
 | 
			
		||||
		UserId:      userId,
 | 
			
		||||
		TaskId:      taskId,
 | 
			
		||||
		Progress:    0,
 | 
			
		||||
		Prompt:      data.Prompt,
 | 
			
		||||
		Power:       h.App.SysConfig.MjActionPower,
 | 
			
		||||
		CreatedAt:   time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
@@ -349,7 +340,6 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
 | 
			
		||||
	h.pool.PushTask(types.MjTask{
 | 
			
		||||
		Id:          job.Id,
 | 
			
		||||
		Type:        types.TaskVariation,
 | 
			
		||||
		Prompt:      data.Prompt,
 | 
			
		||||
		UserId:      userId,
 | 
			
		||||
		Index:       data.Index,
 | 
			
		||||
		ChannelId:   data.ChannelId,
 | 
			
		||||
@@ -397,13 +387,13 @@ func (h *MidJourneyHandler) ImgWall(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
// JobList 获取 MJ 任务列表
 | 
			
		||||
func (h *MidJourneyHandler) JobList(c *gin.Context) {
 | 
			
		||||
	status := h.GetBool(c, "status")
 | 
			
		||||
	finish := h.GetBool(c, "finish")
 | 
			
		||||
	userId := h.GetLoginUserId(c)
 | 
			
		||||
	page := h.GetInt(c, "page", 0)
 | 
			
		||||
	pageSize := h.GetInt(c, "page_size", 0)
 | 
			
		||||
	publish := h.GetBool(c, "publish")
 | 
			
		||||
 | 
			
		||||
	err, jobs := h.getData(status, userId, page, pageSize, publish)
 | 
			
		||||
	err, jobs := h.getData(finish, userId, page, pageSize, publish)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
@@ -459,30 +449,27 @@ func (h *MidJourneyHandler) getData(finish bool, userId uint, page int, pageSize
 | 
			
		||||
 | 
			
		||||
// Remove remove task image
 | 
			
		||||
func (h *MidJourneyHandler) Remove(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Id     uint   `json:"id"`
 | 
			
		||||
		UserId uint   `json:"user_id"`
 | 
			
		||||
		ImgURL string `json:"img_url"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
	id := h.GetInt(c, "id", 0)
 | 
			
		||||
	userId := h.GetInt(c, "user_id", 0)
 | 
			
		||||
	var job model.MidJourneyJob
 | 
			
		||||
	if res := h.DB.Where("id = ? AND user_id = ?", id, userId).First(&job); res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "记录不存在")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove job recode
 | 
			
		||||
	res := h.DB.Delete(&model.MidJourneyJob{Id: data.Id})
 | 
			
		||||
	res := h.DB.Delete(&job)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, res.Error.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove image
 | 
			
		||||
	err := h.uploader.GetUploadHandler().Delete(data.ImgURL)
 | 
			
		||||
	err := h.uploader.GetUploadHandler().Delete(job.ImgURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Error("remove image failed: ", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := h.pool.Clients.Get(data.UserId)
 | 
			
		||||
	client := h.pool.Clients.Get(uint(job.UserId))
 | 
			
		||||
	if client != nil {
 | 
			
		||||
		_ = client.Send([]byte("Task Updated"))
 | 
			
		||||
	}
 | 
			
		||||
@@ -492,16 +479,10 @@ func (h *MidJourneyHandler) Remove(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
// Publish 发布图片到画廊显示
 | 
			
		||||
func (h *MidJourneyHandler) Publish(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Id     uint `json:"id"`
 | 
			
		||||
		Action bool `json:"action"` // 发布动作,true => 发布,false => 取消分享
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res := h.DB.Model(&model.MidJourneyJob{Id: data.Id}).UpdateColumn("publish", data.Action)
 | 
			
		||||
	id := h.GetInt(c, "id", 0)
 | 
			
		||||
	userId := h.GetInt(c, "user_id", 0)
 | 
			
		||||
	action := h.GetBool(c, "action") // 发布动作,true => 发布,false => 取消分享
 | 
			
		||||
	res := h.DB.Model(&model.MidJourneyJob{Id: uint(id), UserId: userId}).UpdateColumn("publish", action)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		logger.Error("error with update database:", res.Error)
 | 
			
		||||
		resp.ERROR(c, "更新数据库失败")
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import (
 | 
			
		||||
	"geekai/store/vo"
 | 
			
		||||
	"geekai/utils"
 | 
			
		||||
	"geekai/utils/resp"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
@@ -27,23 +28,18 @@ func NewOrderHandler(app *core.AppServer, db *gorm.DB) *OrderHandler {
 | 
			
		||||
	return &OrderHandler{BaseHandler: BaseHandler{App: app, DB: db}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// List 订单列表
 | 
			
		||||
func (h *OrderHandler) List(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Page     int `json:"page"`
 | 
			
		||||
		PageSize int `json:"page_size"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	page := h.GetInt(c, "page", 1)
 | 
			
		||||
	pageSize := h.GetInt(c, "page_size", 20)
 | 
			
		||||
	userId := h.GetLoginUserId(c)
 | 
			
		||||
	session := h.DB.Session(&gorm.Session{}).Where("user_id = ? AND status = ?", userId, types.OrderPaidSuccess)
 | 
			
		||||
	var total int64
 | 
			
		||||
	session.Model(&model.Order{}).Count(&total)
 | 
			
		||||
	var items []model.Order
 | 
			
		||||
	var list = make([]vo.Order, 0)
 | 
			
		||||
	offset := (data.Page - 1) * data.PageSize
 | 
			
		||||
	res := session.Order("id DESC").Offset(offset).Limit(data.PageSize).Find(&items)
 | 
			
		||||
	offset := (page - 1) * pageSize
 | 
			
		||||
	res := session.Order("id DESC").Offset(offset).Limit(pageSize).Find(&items)
 | 
			
		||||
	if res.Error == nil {
 | 
			
		||||
		for _, item := range items {
 | 
			
		||||
			var order vo.Order
 | 
			
		||||
@@ -58,5 +54,35 @@ func (h *OrderHandler) List(c *gin.Context) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	resp.SUCCESS(c, vo.NewPage(total, data.Page, data.PageSize, list))
 | 
			
		||||
	resp.SUCCESS(c, vo.NewPage(total, page, pageSize, list))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Query 查询订单状态
 | 
			
		||||
func (h *OrderHandler) Query(c *gin.Context) {
 | 
			
		||||
	orderNo := h.GetTrim(c, "order_no")
 | 
			
		||||
	var order model.Order
 | 
			
		||||
	res := h.DB.Where("order_no = ?", orderNo).First(&order)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "Order not found")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if order.Status == types.OrderPaidSuccess {
 | 
			
		||||
		resp.SUCCESS(c, gin.H{"status": order.Status})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	counter := 0
 | 
			
		||||
	for {
 | 
			
		||||
		time.Sleep(time.Second)
 | 
			
		||||
		var item model.Order
 | 
			
		||||
		h.DB.Where("order_no = ?", orderNo).First(&item)
 | 
			
		||||
		if counter >= 15 || item.Status == types.OrderPaidSuccess || item.Status != order.Status {
 | 
			
		||||
			order.Status = item.Status
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		counter++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.SUCCESS(c, gin.H{"status": order.Status})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,7 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
	// fix: 这里先检查一下订单状态,如果已经支付了,就直接返回
 | 
			
		||||
	if order.Status == types.OrderPaidSuccess {
 | 
			
		||||
		resp.ERROR(c, "This order had been paid, please do not pay twice")
 | 
			
		||||
		resp.ERROR(c, "订单已支付成功,无需重复支付!")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -148,49 +148,11 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
 | 
			
		||||
	resp.ERROR(c, "Invalid operations")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OrderQuery 查询订单状态
 | 
			
		||||
func (h *PaymentHandler) OrderQuery(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		OrderNo string `json:"order_no"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var order model.Order
 | 
			
		||||
	res := h.DB.Where("order_no = ?", data.OrderNo).First(&order)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "Order not found")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if order.Status == types.OrderPaidSuccess {
 | 
			
		||||
		resp.SUCCESS(c, gin.H{"status": order.Status})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	counter := 0
 | 
			
		||||
	for {
 | 
			
		||||
		time.Sleep(time.Second)
 | 
			
		||||
		var item model.Order
 | 
			
		||||
		h.DB.Where("order_no = ?", data.OrderNo).First(&item)
 | 
			
		||||
		if counter >= 15 || item.Status == types.OrderPaidSuccess || item.Status != order.Status {
 | 
			
		||||
			order.Status = item.Status
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		counter++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.SUCCESS(c, gin.H{"status": order.Status})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PayQrcode 生成支付 URL 二维码
 | 
			
		||||
func (h *PaymentHandler) PayQrcode(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		PayWay    string `json:"pay_way"` // 支付方式
 | 
			
		||||
		ProductId uint   `json:"product_id"`
 | 
			
		||||
		UserId    int    `json:"user_id"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
@@ -209,10 +171,9 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
 | 
			
		||||
		resp.ERROR(c, "error with generate trade no: "+err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var user model.User
 | 
			
		||||
	res = h.DB.First(&user, data.UserId)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "Invalid user ID")
 | 
			
		||||
	user, err := h.GetLoginUser(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.NotAuth(c)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -333,7 +294,6 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		PayWay    string `json:"pay_way"` // 支付方式
 | 
			
		||||
		ProductId uint   `json:"product_id"`
 | 
			
		||||
		UserId    int    `json:"user_id"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
@@ -352,10 +312,9 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
 | 
			
		||||
		resp.ERROR(c, "error with generate trade no: "+err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var user model.User
 | 
			
		||||
	res = h.DB.First(&user, data.UserId)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "Invalid user ID")
 | 
			
		||||
	user, err := h.GetLoginUser(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.NotAuth(c)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -449,7 +408,7 @@ func (h *PaymentHandler) Mobile(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.SUCCESS(c, payURL)
 | 
			
		||||
	resp.SUCCESS(c, gin.H{"url": payURL, "order_no": orderNo})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 异步通知回调公共逻辑
 | 
			
		||||
 
 | 
			
		||||
@@ -99,10 +99,7 @@ func (h *SdJobHandler) Image(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var data struct {
 | 
			
		||||
		SessionId string `json:"session_id"`
 | 
			
		||||
		types.SdTaskParams
 | 
			
		||||
	}
 | 
			
		||||
	var data types.SdTaskParams
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil || data.Prompt == "" {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
@@ -215,13 +212,13 @@ func (h *SdJobHandler) ImgWall(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
// JobList 获取 SD 任务列表
 | 
			
		||||
func (h *SdJobHandler) JobList(c *gin.Context) {
 | 
			
		||||
	status := h.GetBool(c, "status")
 | 
			
		||||
	finish := h.GetBool(c, "finish")
 | 
			
		||||
	userId := h.GetLoginUserId(c)
 | 
			
		||||
	page := h.GetInt(c, "page", 0)
 | 
			
		||||
	pageSize := h.GetInt(c, "page_size", 0)
 | 
			
		||||
	publish := h.GetBool(c, "publish")
 | 
			
		||||
 | 
			
		||||
	err, jobs := h.getData(status, userId, page, pageSize, publish)
 | 
			
		||||
	err, jobs := h.getData(finish, userId, page, pageSize, publish)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
@@ -280,30 +277,28 @@ func (h *SdJobHandler) getData(finish bool, userId uint, page int, pageSize int,
 | 
			
		||||
 | 
			
		||||
// Remove remove task image
 | 
			
		||||
func (h *SdJobHandler) Remove(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Id     uint   `json:"id"`
 | 
			
		||||
		UserId uint   `json:"user_id"`
 | 
			
		||||
		ImgURL string `json:"img_url"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
	id := h.GetInt(c, "id", 0)
 | 
			
		||||
	userId := h.GetInt(c, "user_id", 0)
 | 
			
		||||
	var job model.SdJob
 | 
			
		||||
	if res := h.DB.Where("id = ? AND user_id = ?", id, userId).First(&job); res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "记录不存在")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove job recode
 | 
			
		||||
	res := h.DB.Delete(&model.SdJob{Id: data.Id})
 | 
			
		||||
	res := h.DB.Delete(&model.SdJob{Id: job.Id})
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, res.Error.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remove image
 | 
			
		||||
	err := h.uploader.GetUploadHandler().Delete(data.ImgURL)
 | 
			
		||||
	err := h.uploader.GetUploadHandler().Delete(job.ImgURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Error("remove image failed: ", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := h.pool.Clients.Get(data.UserId)
 | 
			
		||||
	client := h.pool.Clients.Get(uint(job.UserId))
 | 
			
		||||
	if client != nil {
 | 
			
		||||
		_ = client.Send([]byte(sd.Finished))
 | 
			
		||||
	}
 | 
			
		||||
@@ -313,16 +308,11 @@ func (h *SdJobHandler) Remove(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
// Publish 发布/取消发布图片到画廊显示
 | 
			
		||||
func (h *SdJobHandler) Publish(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Id     uint `json:"id"`
 | 
			
		||||
		Action bool `json:"action"` // 发布动作,true => 发布,false => 取消分享
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	id := h.GetInt(c, "id", 0)
 | 
			
		||||
	userId := h.GetInt(c, "user_id", 0)
 | 
			
		||||
	action := h.GetBool(c, "action") // 发布动作,true => 发布,false => 取消分享
 | 
			
		||||
 | 
			
		||||
	res := h.DB.Model(&model.SdJob{Id: data.Id}).UpdateColumn("publish", true)
 | 
			
		||||
	res := h.DB.Model(&model.SdJob{Id: uint(id), UserId: userId}).UpdateColumn("publish", action)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		logger.Error("error with update database:", res.Error)
 | 
			
		||||
		resp.ERROR(c, "更新数据库失败")
 | 
			
		||||
 
 | 
			
		||||
@@ -49,14 +49,20 @@ func (h *SmsHandler) SendCode(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Receiver string `json:"receiver"` // 接收者
 | 
			
		||||
		Key      string `json:"key"`
 | 
			
		||||
		Dots     string `json:"dots"`
 | 
			
		||||
		Dots     string `json:"dots,omitempty"`
 | 
			
		||||
		X        int    `json:"x,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !h.captcha.Check(data) {
 | 
			
		||||
	var check bool
 | 
			
		||||
	if data.X != 0 {
 | 
			
		||||
		check = h.captcha.SlideCheck(data)
 | 
			
		||||
	} else {
 | 
			
		||||
		check = h.captcha.Check(data)
 | 
			
		||||
	}
 | 
			
		||||
	if !check {
 | 
			
		||||
		resp.ERROR(c, "验证码错误,请先完人机验证")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -89,5 +95,9 @@ func (h *SmsHandler) SendCode(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resp.SUCCESS(c)
 | 
			
		||||
	if h.App.Debug {
 | 
			
		||||
		resp.SUCCESS(c, code)
 | 
			
		||||
	} else {
 | 
			
		||||
		resp.SUCCESS(c)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,7 @@ func (h *UploadHandler) Upload(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
func (h *UploadHandler) List(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Urls []string `json:"urls"`
 | 
			
		||||
		Urls []string `json:"urls,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
 
 | 
			
		||||
@@ -185,7 +185,7 @@ func (h *UserHandler) Register(c *gin.Context) {
 | 
			
		||||
		resp.ERROR(c, "error with save token: "+err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	resp.SUCCESS(c, tokenString)
 | 
			
		||||
	resp.SUCCESS(c, gin.H{"token": tokenString, "user_id": user.Id, "username": user.Username})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Login 用户登录
 | 
			
		||||
@@ -244,7 +244,7 @@ func (h *UserHandler) Login(c *gin.Context) {
 | 
			
		||||
		resp.ERROR(c, "error with save token: "+err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	resp.SUCCESS(c, tokenString)
 | 
			
		||||
	resp.SUCCESS(c, gin.H{"token": tokenString, "user_id": user.Id, "username": user.Username})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Logout 注 销
 | 
			
		||||
@@ -256,8 +256,8 @@ func (h *UserHandler) Logout(c *gin.Context) {
 | 
			
		||||
	resp.SUCCESS(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CLoginRequest 第三方登录请求二维码
 | 
			
		||||
func (h *UserHandler) CLoginRequest(c *gin.Context) {
 | 
			
		||||
// Clogin 第三方登录请求二维码
 | 
			
		||||
func (h *UserHandler) Clogin(c *gin.Context) {
 | 
			
		||||
	returnURL := h.GetTrim(c, "return_url")
 | 
			
		||||
	var res types.BizVo
 | 
			
		||||
	apiURL := fmt.Sprintf("%s/api/clogin/request", h.App.Config.ApiConfig.ApiURL)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								api/main.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								api/main.go
									
									
									
									
									
								
							@@ -240,7 +240,7 @@ func main() {
 | 
			
		||||
			group.POST("password", h.UpdatePass)
 | 
			
		||||
			group.POST("bind/username", h.BindUsername)
 | 
			
		||||
			group.POST("resetPass", h.ResetPass)
 | 
			
		||||
			group.GET("clogin/request", h.CLoginRequest)
 | 
			
		||||
			group.GET("clogin", h.Clogin)
 | 
			
		||||
			group.GET("clogin/callback", h.CLoginCallback)
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *chatimpl.ChatHandler) {
 | 
			
		||||
@@ -283,8 +283,8 @@ func main() {
 | 
			
		||||
			group.POST("variation", h.Variation)
 | 
			
		||||
			group.GET("jobs", h.JobList)
 | 
			
		||||
			group.GET("imgWall", h.ImgWall)
 | 
			
		||||
			group.POST("remove", h.Remove)
 | 
			
		||||
			group.POST("publish", h.Publish)
 | 
			
		||||
			group.GET("remove", h.Remove)
 | 
			
		||||
			group.GET("publish", h.Publish)
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *handler.SdJobHandler) {
 | 
			
		||||
			group := s.Engine.Group("/api/sd")
 | 
			
		||||
@@ -292,8 +292,8 @@ func main() {
 | 
			
		||||
			group.POST("image", h.Image)
 | 
			
		||||
			group.GET("jobs", h.JobList)
 | 
			
		||||
			group.GET("imgWall", h.ImgWall)
 | 
			
		||||
			group.POST("remove", h.Remove)
 | 
			
		||||
			group.POST("publish", h.Publish)
 | 
			
		||||
			group.GET("remove", h.Remove)
 | 
			
		||||
			group.GET("publish", h.Publish)
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *handler.ConfigHandler) {
 | 
			
		||||
			group := s.Engine.Group("/api/config/")
 | 
			
		||||
@@ -370,7 +370,6 @@ func main() {
 | 
			
		||||
			group := s.Engine.Group("/api/payment/")
 | 
			
		||||
			group.GET("doPay", h.DoPay)
 | 
			
		||||
			group.GET("payWays", h.GetPayWays)
 | 
			
		||||
			group.POST("query", h.OrderQuery)
 | 
			
		||||
			group.POST("qrcode", h.PayQrcode)
 | 
			
		||||
			group.POST("mobile", h.Mobile)
 | 
			
		||||
			group.POST("alipay/notify", h.AlipayNotify)
 | 
			
		||||
@@ -393,7 +392,8 @@ func main() {
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *handler.OrderHandler) {
 | 
			
		||||
			group := s.Engine.Group("/api/order/")
 | 
			
		||||
			group.POST("list", h.List)
 | 
			
		||||
			group.GET("list", h.List)
 | 
			
		||||
			group.GET("query", h.Query)
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, h *handler.ProductHandler) {
 | 
			
		||||
			group := s.Engine.Group("/api/product/")
 | 
			
		||||
@@ -472,8 +472,8 @@ func main() {
 | 
			
		||||
			group.POST("image", h.Image)
 | 
			
		||||
			group.GET("jobs", h.JobList)
 | 
			
		||||
			group.GET("imgWall", h.ImgWall)
 | 
			
		||||
			group.POST("remove", h.Remove)
 | 
			
		||||
			group.POST("publish", h.Publish)
 | 
			
		||||
			group.GET("remove", h.Remove)
 | 
			
		||||
			group.GET("publish", h.Publish)
 | 
			
		||||
		}),
 | 
			
		||||
		fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
 | 
			
		||||
			go func() {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,28 +24,20 @@ func SUCCESS(c *gin.Context, values ...interface{}) {
 | 
			
		||||
 | 
			
		||||
func ERROR(c *gin.Context, messages ...string) {
 | 
			
		||||
	if messages != nil {
 | 
			
		||||
		c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: messages[0]})
 | 
			
		||||
		c.JSON(http.StatusBadRequest, types.BizVo{Code: types.Failed, Message: messages[0]})
 | 
			
		||||
	} else {
 | 
			
		||||
		c.JSON(http.StatusOK, types.BizVo{Code: types.Failed})
 | 
			
		||||
		c.JSON(http.StatusBadRequest, types.BizVo{Code: types.Failed})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func HACKER(c *gin.Context) {
 | 
			
		||||
	c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Hacker attempt!!!"})
 | 
			
		||||
	c.JSON(http.StatusBadRequest, types.BizVo{Code: types.Failed, Message: "Hacker attempt!!!"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NotAuth(c *gin.Context, messages ...string) {
 | 
			
		||||
	if messages != nil {
 | 
			
		||||
		c.JSON(http.StatusOK, types.BizVo{Code: types.NotAuthorized, Message: messages[0]})
 | 
			
		||||
		c.JSON(http.StatusUnauthorized, types.BizVo{Code: types.NotAuthorized, Message: messages[0]})
 | 
			
		||||
	} else {
 | 
			
		||||
		c.JSON(http.StatusOK, types.BizVo{Code: types.NotAuthorized, Message: "Not Authorized"})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NotPermission(c *gin.Context, messages ...string) {
 | 
			
		||||
	if messages != nil {
 | 
			
		||||
		c.JSON(http.StatusOK, types.BizVo{Code: types.NotPermission, Message: messages[0]})
 | 
			
		||||
	} else {
 | 
			
		||||
		c.JSON(http.StatusOK, types.BizVo{Code: types.NotPermission, Message: "Not Permission"})
 | 
			
		||||
		c.JSON(http.StatusUnauthorized, types.BizVo{Code: types.NotAuthorized, Message: "Not Authorized"})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
ALTER TABLE `chatgpt_chat_models` CHANGE `power` `power` SMALLINT NOT NULL COMMENT '消耗算力点数';
 | 
			
		||||
ALTER TABLE `chatgpt_users` ADD `openid` VARCHAR(100) NULL COMMENT '第三方登录账号ID' AFTER `last_login_ip`;
 | 
			
		||||
ALTER TABLE `chatgpt_users` ADD UNIQUE(`openid`);
 | 
			
		||||
ALTER TABLE `chatgpt_users` ADD `platform` VARCHAR(30) NULL COMMENT '登录平台' AFTER `openid`;
 | 
			
		||||
ALTER TABLE `chatgpt_users` CHANGE `avatar` `avatar` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头像';
 | 
			
		||||
ALTER TABLE `chatgpt_chat_history` CHANGE `icon` `icon` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色图标';
 | 
			
		||||
ALTER TABLE `chatgpt_chat_history` CHANGE `icon` `icon` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色图标';
 | 
			
		||||
ALTER TABLE `chatgpt_orders` CHANGE `status` `status` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '订单状态(0:待支付,1:已扫码,2:支付成功)';
 | 
			
		||||
@@ -46,8 +46,8 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {onMounted, ref, watch} from "vue";
 | 
			
		||||
import {httpPost} from "@/utils/http";
 | 
			
		||||
import {onMounted, ref} from "vue";
 | 
			
		||||
import {httpGet} from "@/utils/http";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
import {dateFormat} from "@/utils/libs";
 | 
			
		||||
import {DocumentCopy} from "@element-plus/icons-vue";
 | 
			
		||||
@@ -73,7 +73,7 @@ onMounted(() => {
 | 
			
		||||
 | 
			
		||||
// 获取数据
 | 
			
		||||
const fetchData = () => {
 | 
			
		||||
  httpPost('/api/order/list', {page: page.value, page_size: pageSize.value}).then((res) => {
 | 
			
		||||
  httpGet('/api/order/list', {page: page.value, page_size: pageSize.value}).then((res) => {
 | 
			
		||||
    if (res.data) {
 | 
			
		||||
      items.value = res.data.items
 | 
			
		||||
      total.value = res.data.total
 | 
			
		||||
 
 | 
			
		||||
@@ -26,17 +26,15 @@ axios.interceptors.request.use(
 | 
			
		||||
    })
 | 
			
		||||
axios.interceptors.response.use(
 | 
			
		||||
    response => {
 | 
			
		||||
        let data = response.data;
 | 
			
		||||
        if (data.code === 0) {
 | 
			
		||||
            return response
 | 
			
		||||
        } else if (data.code === 400) {
 | 
			
		||||
        if (response.status === 401) {
 | 
			
		||||
            if (response.request.responseURL.indexOf("/api/admin") !== -1) {
 | 
			
		||||
                removeAdminToken()
 | 
			
		||||
            } else {
 | 
			
		||||
                removeUserToken()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
            return Promise.reject(response.data)
 | 
			
		||||
        }
 | 
			
		||||
        return response
 | 
			
		||||
    }, error => {
 | 
			
		||||
        return Promise.reject(error)
 | 
			
		||||
    })
 | 
			
		||||
 
 | 
			
		||||
@@ -557,6 +557,7 @@ const socket = ref(null);
 | 
			
		||||
const activelyClose = ref(false); // 主动关闭
 | 
			
		||||
const canSend = ref(true);
 | 
			
		||||
const heartbeatHandle = ref(null)
 | 
			
		||||
const sessionId = ref("")
 | 
			
		||||
const connect = function (chat_id, role_id) {
 | 
			
		||||
  let isNewChat = false;
 | 
			
		||||
  if (!chat_id) {
 | 
			
		||||
@@ -571,7 +572,7 @@ const connect = function (chat_id, role_id) {
 | 
			
		||||
 | 
			
		||||
  const _role = getRoleById(role_id);
 | 
			
		||||
  // 初始化 WebSocket 对象
 | 
			
		||||
  const _sessionId = getSessionId();
 | 
			
		||||
  sessionId.value = getSessionId();
 | 
			
		||||
  let host = process.env.VUE_APP_WS_HOST
 | 
			
		||||
  if (host === '') {
 | 
			
		||||
    if (location.protocol === 'https:') {
 | 
			
		||||
@@ -593,7 +594,7 @@ const connect = function (chat_id, role_id) {
 | 
			
		||||
      heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${role_id}&chat_id=${chat_id}&model_id=${modelID.value}&token=${getUserToken()}`);
 | 
			
		||||
  const _socket = new WebSocket(host + `/api/chat/new?session_id=${sessionId.value}&role_id=${role_id}&chat_id=${chat_id}&model_id=${modelID.value}&token=${getUserToken()}`);
 | 
			
		||||
  _socket.addEventListener('open', () => {
 | 
			
		||||
    chatData.value = []; // 初始化聊天数据
 | 
			
		||||
    enableInput()
 | 
			
		||||
@@ -852,7 +853,7 @@ const loadChatHistory = function (chatId) {
 | 
			
		||||
 | 
			
		||||
const stopGenerate = function () {
 | 
			
		||||
  showStopGenerate.value = false;
 | 
			
		||||
  httpGet("/api/chat/stop?session_id=" + getSessionId()).then(() => {
 | 
			
		||||
  httpGet("/api/chat/stop?session_id=" + sessionId.value).then(() => {
 | 
			
		||||
    enableInput()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -334,7 +334,7 @@ const fetchRunningJobs = () => {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  // 获取运行中的任务
 | 
			
		||||
  httpGet(`/api/dall/jobs?status=0`).then(res => {
 | 
			
		||||
  httpGet(`/api/dall/jobs?finish=false`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    const _jobs = []
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
@@ -367,7 +367,7 @@ const fetchFinishJobs = () => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  page.value = page.value + 1
 | 
			
		||||
 | 
			
		||||
  httpGet(`/api/dall/jobs?status=1&page=${page.value}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
  httpGet(`/api/dall/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
    if (res.data.length < pageSize.value) {
 | 
			
		||||
      isOver.value = true
 | 
			
		||||
    }
 | 
			
		||||
@@ -419,7 +419,7 @@ const removeImage = (event, item) => {
 | 
			
		||||
        type: 'warning',
 | 
			
		||||
      }
 | 
			
		||||
  ).then(() => {
 | 
			
		||||
    httpPost("/api/dall/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
 | 
			
		||||
    httpGet("/api/dall/remove", {id: item.id, user_id: item.user}).then(() => {
 | 
			
		||||
      ElMessage.success("任务删除成功")
 | 
			
		||||
      page.value = 0
 | 
			
		||||
      isOver.value = false
 | 
			
		||||
@@ -442,7 +442,7 @@ const publishImage = (event, item, action) => {
 | 
			
		||||
  if (action === false) {
 | 
			
		||||
    text = "取消发布"
 | 
			
		||||
  }
 | 
			
		||||
  httpPost("/api/dall/publish", {id: item.id, action: action}).then(() => {
 | 
			
		||||
  httpGet("/api/dall/publish", {id: item.id, action: action,user_id:item.user_id}).then(() => {
 | 
			
		||||
    ElMessage.success(text + "成功")
 | 
			
		||||
    item.publish = action
 | 
			
		||||
    page.value = 0
 | 
			
		||||
 
 | 
			
		||||
@@ -782,7 +782,7 @@ const fetchRunningJobs = () => {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  httpGet(`/api/mj/jobs?status=0`).then(res => {
 | 
			
		||||
  httpGet(`/api/mj/jobs?finish=false`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    const _jobs = []
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
@@ -820,7 +820,7 @@ const fetchFinishJobs = () => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  page.value = page.value + 1
 | 
			
		||||
  // 获取已完成的任务
 | 
			
		||||
  httpGet(`/api/mj/jobs?status=1&page=${page.value}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
  httpGet(`/api/mj/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
      if (jobs[i]['img_url'] !== "") {
 | 
			
		||||
@@ -961,7 +961,7 @@ const removeImage = (item) => {
 | 
			
		||||
        type: 'warning',
 | 
			
		||||
      }
 | 
			
		||||
  ).then(() => {
 | 
			
		||||
    httpPost("/api/mj/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
 | 
			
		||||
    httpGet("/api/mj/remove", {id: item.id, user_id: item.user_id}).then(() => {
 | 
			
		||||
      ElMessage.success("任务删除成功")
 | 
			
		||||
      page.value = 0
 | 
			
		||||
      isOver.value = false
 | 
			
		||||
@@ -979,7 +979,7 @@ const publishImage = (item, action) => {
 | 
			
		||||
  if (action === false) {
 | 
			
		||||
    text = "取消发布"
 | 
			
		||||
  }
 | 
			
		||||
  httpPost("/api/mj/publish", {id: item.id, action: action}).then(() => {
 | 
			
		||||
  httpGet("/api/mj/publish", {id: item.id, action: action,user_id: item.user_id}).then(() => {
 | 
			
		||||
    ElMessage.success(text + "成功")
 | 
			
		||||
    item.publish = action
 | 
			
		||||
    page.value = 0
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
              </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-select v-model="params.scheduler" style="width:176px">
 | 
			
		||||
@@ -631,7 +631,7 @@ const fetchRunningJobs = () => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 获取运行中的任务
 | 
			
		||||
  httpGet(`/api/sd/jobs?status=0`).then(res => {
 | 
			
		||||
  httpGet(`/api/sd/jobs?finish=0`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    const _jobs = []
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
@@ -664,7 +664,7 @@ const fetchFinishJobs = () => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  page.value = page.value + 1
 | 
			
		||||
 | 
			
		||||
  httpGet(`/api/sd/jobs?status=1&page=${page.value}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
  httpGet(`/api/sd/jobs?finish=1&page=${page.value}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
    if (res.data.length < pageSize.value) {
 | 
			
		||||
      isOver.value = true
 | 
			
		||||
    }
 | 
			
		||||
@@ -731,7 +731,7 @@ const removeImage = (event, item) => {
 | 
			
		||||
        type: 'warning',
 | 
			
		||||
      }
 | 
			
		||||
  ).then(() => {
 | 
			
		||||
    httpPost("/api/sd/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
 | 
			
		||||
    httpGet("/api/sd/remove", {id: item.id, user_id: item.user}).then(() => {
 | 
			
		||||
      ElMessage.success("任务删除成功")
 | 
			
		||||
      page.value = 0
 | 
			
		||||
      isOver.value = false
 | 
			
		||||
@@ -750,7 +750,7 @@ const publishImage = (event, item, action) => {
 | 
			
		||||
  if (action === false) {
 | 
			
		||||
    text = "取消发布"
 | 
			
		||||
  }
 | 
			
		||||
  httpPost("/api/sd/publish", {id: item.id, action: action}).then(() => {
 | 
			
		||||
  httpGet("/api/sd/publish", {id: item.id, action: action, user_id: item.user}).then(() => {
 | 
			
		||||
    ElMessage.success(text + "成功")
 | 
			
		||||
    item.publish = action
 | 
			
		||||
    page.value = 0
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ onMounted(() => {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const returnURL = `${location.protocol}//${location.host}/login/callback`
 | 
			
		||||
  httpGet("/api/user/clogin/request?return_url="+returnURL).then(res => {
 | 
			
		||||
  httpGet("/api/user/clogin?return_url="+returnURL).then(res => {
 | 
			
		||||
    wechatLoginURL.value = res.data.url
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    console.error(e)
 | 
			
		||||
@@ -132,7 +132,7 @@ const login = function () {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  httpPost('/api/user/login', {username: username.value.trim(), password: password.value.trim()}).then((res) => {
 | 
			
		||||
    setUserToken(res.data)
 | 
			
		||||
    setUserToken(res.data.token)
 | 
			
		||||
    if (isMobile()) {
 | 
			
		||||
      router.push('/mobile')
 | 
			
		||||
    } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -333,7 +333,7 @@ const wechatPay = (row) => {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const queryOrder = (orderNo) => {
 | 
			
		||||
  httpPost("/api/payment/query", {order_no: orderNo}).then(res => {
 | 
			
		||||
  httpGet("/api/order/query", {order_no: orderNo}).then(res => {
 | 
			
		||||
    if (res.data.status === 1) {
 | 
			
		||||
      text.value = "扫码成功,请在手机上进行支付!"
 | 
			
		||||
      queryOrder(orderNo)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,637 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="mobile-mj">
 | 
			
		||||
    <van-form @submit="generate">
 | 
			
		||||
      <div class="text-line">图片比例</div>
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <van-row :gutter="10">
 | 
			
		||||
          <van-col :span="4" v-for="item in rates" :key="item.value">
 | 
			
		||||
            <div
 | 
			
		||||
                :class="item.value === params.rate ? 'rate active' : 'rate'"
 | 
			
		||||
                @click="changeRate(item)">
 | 
			
		||||
              <div class="icon">
 | 
			
		||||
                <van-image :src="item.img" fit="cover"></van-image>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="text">{{ item.text }}</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </van-col>
 | 
			
		||||
        </van-row>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="text-line">模型选择</div>
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <van-row :gutter="10">
 | 
			
		||||
          <van-col :span="8" v-for="item in models" :key="item.value">
 | 
			
		||||
            <div :class="item.value === params.model ? 'model active' : 'model'"
 | 
			
		||||
                 @click="changeModel(item)">
 | 
			
		||||
              <div class="icon">
 | 
			
		||||
                <van-image :src="item.img" fit="cover"></van-image>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="text">
 | 
			
		||||
                <van-text-ellipsis :content="item.text"/>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </van-col>
 | 
			
		||||
        </van-row>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <van-field label="创意度">
 | 
			
		||||
          <template #input>
 | 
			
		||||
            <van-slider v-model.number="params.chaos" :max="100" :step="1"
 | 
			
		||||
                        @update:model-value="showToast('当前值:' + params.chaos)"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </van-field>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <van-field label="风格化">
 | 
			
		||||
          <template #input>
 | 
			
		||||
            <van-slider v-model.number="params.stylize" :max="1000" :step="1"
 | 
			
		||||
                        @update:model-value="showToast('当前值:' + params.stylize)"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </van-field>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <van-field label="原始模式">
 | 
			
		||||
          <template #input>
 | 
			
		||||
            <van-switch v-model="params.raw"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </van-field>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <van-tabs v-model:active="activeName" @change="tabChange" animated>
 | 
			
		||||
          <van-tab title="文生图" name="txt2img">
 | 
			
		||||
            <div class="text-line">
 | 
			
		||||
              <van-field v-model="params.prompt"
 | 
			
		||||
                         rows="3"
 | 
			
		||||
                         autosize
 | 
			
		||||
                         type="textarea"
 | 
			
		||||
                         placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
 | 
			
		||||
            </div>
 | 
			
		||||
          </van-tab>
 | 
			
		||||
          <van-tab title="图生图" name="img2img">
 | 
			
		||||
            <div class="text-line">
 | 
			
		||||
              <van-field v-model="params.prompt"
 | 
			
		||||
                         rows="3"
 | 
			
		||||
                         autosize
 | 
			
		||||
                         type="textarea"
 | 
			
		||||
                         placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="text-line">
 | 
			
		||||
              <van-uploader v-model="imgList" :after-read="uploadImg"/>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="text-line">
 | 
			
		||||
              <van-field label="垫图权重">
 | 
			
		||||
                <template #input>
 | 
			
		||||
                  <van-slider v-model.number="params.iw" :max="1" :step="0.01"
 | 
			
		||||
                              @update:model-value="showToast('当前值:' + params.iw)"/>
 | 
			
		||||
                </template>
 | 
			
		||||
              </van-field>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="tip-text">提示:只有于 niji6 和 v6 模型支持一致性功能,如果选择其他模型此功能将会生成失败。</div>
 | 
			
		||||
            <van-cell-group>
 | 
			
		||||
              <van-field
 | 
			
		||||
                  v-model="params.cref"
 | 
			
		||||
                  center
 | 
			
		||||
                  clearable
 | 
			
		||||
                  label="角色一致性"
 | 
			
		||||
                  placeholder="请输入图片URL或者上传图片"
 | 
			
		||||
              >
 | 
			
		||||
                <template #button>
 | 
			
		||||
                  <van-uploader @click="beforeUpload('cref')" :after-read="uploadImg">
 | 
			
		||||
                    <van-button size="mini" type="primary" icon="plus"/>
 | 
			
		||||
                  </van-uploader>
 | 
			
		||||
                </template>
 | 
			
		||||
              </van-field>
 | 
			
		||||
            </van-cell-group>
 | 
			
		||||
 | 
			
		||||
            <van-cell-group>
 | 
			
		||||
              <van-field
 | 
			
		||||
                  v-model="params.sref"
 | 
			
		||||
                  center
 | 
			
		||||
                  clearable
 | 
			
		||||
                  label="风格一致性"
 | 
			
		||||
                  placeholder="请输入图片URL或者上传图片"
 | 
			
		||||
              >
 | 
			
		||||
                <template #button>
 | 
			
		||||
                  <van-uploader @click="beforeUpload('sref')" :after-read="uploadImg">
 | 
			
		||||
                    <van-button size="mini" type="primary" icon="plus"/>
 | 
			
		||||
                  </van-uploader>
 | 
			
		||||
                </template>
 | 
			
		||||
              </van-field>
 | 
			
		||||
            </van-cell-group>
 | 
			
		||||
 | 
			
		||||
            <div class="text-line">
 | 
			
		||||
              <van-field label="一致性权重">
 | 
			
		||||
                <template #input>
 | 
			
		||||
                  <van-slider v-model.number="params.cw" :max="100" :step="1"
 | 
			
		||||
                              @update:model-value="showToast('当前值:' + params.cw)"/>
 | 
			
		||||
                </template>
 | 
			
		||||
              </van-field>
 | 
			
		||||
            </div>
 | 
			
		||||
          </van-tab>
 | 
			
		||||
          <van-tab title="融图" name="blend">
 | 
			
		||||
            <div class="tip-text">请上传两张以上的图片,最多不超过五张,超过五张图片请使用图生图功能。</div>
 | 
			
		||||
            <div class="text-line">
 | 
			
		||||
              <van-uploader v-model="imgList" :after-read="uploadImg"/>
 | 
			
		||||
            </div>
 | 
			
		||||
          </van-tab>
 | 
			
		||||
          <van-tab title="换脸" name="swapFace">
 | 
			
		||||
            <div class="tip-text">请上传两张有脸部的图片,用左边图片的脸替换右边图片的脸。</div>
 | 
			
		||||
            <div class="text-line">
 | 
			
		||||
              <van-uploader v-model="imgList" :after-read="uploadImg"/>
 | 
			
		||||
            </div>
 | 
			
		||||
          </van-tab>
 | 
			
		||||
        </van-tabs>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <van-collapse v-model="activeColspan">
 | 
			
		||||
          <van-collapse-item title="反向提示词" name="neg_prompt">
 | 
			
		||||
            <van-field
 | 
			
		||||
                v-model="params.neg_prompt"
 | 
			
		||||
                rows="3"
 | 
			
		||||
                autosize
 | 
			
		||||
                type="textarea"
 | 
			
		||||
                placeholder="不想出现在图片上的元素(例如:树,建筑)"
 | 
			
		||||
            />
 | 
			
		||||
          </van-collapse-item>
 | 
			
		||||
        </van-collapse>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <el-tag>绘图消耗{{ mjPower }}算力,U/V 操作消耗{{ mjActionPower }}算力,当前算力:{{ power }}</el-tag>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="text-line">
 | 
			
		||||
        <van-button round block type="primary" native-type="submit">
 | 
			
		||||
          立即生成
 | 
			
		||||
        </van-button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </van-form>
 | 
			
		||||
 | 
			
		||||
    <h3>任务列表</h3>
 | 
			
		||||
    <div class="running-job-list">
 | 
			
		||||
      <van-empty v-if="runningJobs.length ===0"
 | 
			
		||||
                 image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
 | 
			
		||||
                 image-size="80"
 | 
			
		||||
                 description="暂无记录"
 | 
			
		||||
      />
 | 
			
		||||
      <van-grid :gutter="10" :column-num="3" v-else>
 | 
			
		||||
        <van-grid-item v-for="item in runningJobs">
 | 
			
		||||
          <div v-if="item.progress > 0">
 | 
			
		||||
            <van-image :src="item['img_url']">
 | 
			
		||||
              <template v-slot:error>加载失败</template>
 | 
			
		||||
            </van-image>
 | 
			
		||||
            <div class="progress">
 | 
			
		||||
              <van-circle
 | 
			
		||||
                  v-model:current-rate="item.progress"
 | 
			
		||||
                  :rate="item.progress"
 | 
			
		||||
                  :speed="100"
 | 
			
		||||
                  :text="item.progress+'%'"
 | 
			
		||||
                  :stroke-width="60"
 | 
			
		||||
                  size="90px"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div v-else class="task-in-queue">
 | 
			
		||||
            <span class="icon"><i class="iconfont icon-quick-start"></i></span>
 | 
			
		||||
            <span class="text">排队中</span>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
        </van-grid-item>
 | 
			
		||||
      </van-grid>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <h3>创作记录</h3>
 | 
			
		||||
    <div class="finish-job-list">
 | 
			
		||||
      <van-empty v-if="finishedJobs.length ===0"
 | 
			
		||||
                 image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
 | 
			
		||||
                 image-size="80"
 | 
			
		||||
                 description="暂无记录"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <van-list v-else
 | 
			
		||||
                v-model:error="error"
 | 
			
		||||
                v-model:loading="loading"
 | 
			
		||||
                :finished="finished"
 | 
			
		||||
                error-text="请求失败,点击重新加载"
 | 
			
		||||
                finished-text="没有更多了"
 | 
			
		||||
                @load="onLoad"
 | 
			
		||||
      >
 | 
			
		||||
        <van-grid :gutter="10" :column-num="2">
 | 
			
		||||
          <van-grid-item v-for="item in finishedJobs">
 | 
			
		||||
            <div class="job-item">
 | 
			
		||||
              <van-image
 | 
			
		||||
                  :src="item['thumb_url']"
 | 
			
		||||
                  :class="item['can_opt'] ? '' : 'upscale'"
 | 
			
		||||
                  lazy-load
 | 
			
		||||
                  @click="imageView(item)"
 | 
			
		||||
                  fit="cover">
 | 
			
		||||
                <template v-slot:loading>
 | 
			
		||||
                  <van-loading type="spinner" size="20"/>
 | 
			
		||||
                </template>
 | 
			
		||||
              </van-image>
 | 
			
		||||
 | 
			
		||||
              <div class="opt" v-if="item['can_opt']">
 | 
			
		||||
 | 
			
		||||
                <van-grid :gutter="3" :column-num="4">
 | 
			
		||||
                  <van-grid-item><a @click="upscale(1, item)" class="opt-btn">U1</a></van-grid-item>
 | 
			
		||||
                  <van-grid-item><a @click="upscale(2, item)" class="opt-btn">U2</a></van-grid-item>
 | 
			
		||||
                  <van-grid-item><a @click="upscale(3, item)" class="opt-btn">U3</a></van-grid-item>
 | 
			
		||||
                  <van-grid-item><a @click="upscale(4, item)" class="opt-btn">U4</a></van-grid-item>
 | 
			
		||||
                  <van-grid-item><a @click="variation(1, item)" class="opt-btn">V1</a></van-grid-item>
 | 
			
		||||
                  <van-grid-item><a @click="variation(2, item)" class="opt-btn">V2</a></van-grid-item>
 | 
			
		||||
                  <van-grid-item><a @click="variation(3, item)" class="opt-btn">V3</a></van-grid-item>
 | 
			
		||||
                  <van-grid-item><a @click="variation(4, item)" class="opt-btn">V4</a></van-grid-item>
 | 
			
		||||
                </van-grid>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div class="remove">
 | 
			
		||||
                <el-button type="danger" :icon="Delete" @click="removeImage(item)" circle/>
 | 
			
		||||
                <el-button type="warning" v-if="item.publish" @click="publishImage(item, false)"
 | 
			
		||||
                           circle>
 | 
			
		||||
                  <i class="iconfont icon-cancel-share"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
                <el-button type="success" v-else @click="publishImage(item, true)" circle>
 | 
			
		||||
                  <i class="iconfont icon-share-bold"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
                <el-button type="primary" @click="showPrompt(item)" circle>
 | 
			
		||||
                  <i class="iconfont icon-prompt"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </van-grid-item>
 | 
			
		||||
        </van-grid>
 | 
			
		||||
      </van-list>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {nextTick, onMounted, onUnmounted, ref} from "vue";
 | 
			
		||||
import {
 | 
			
		||||
  showConfirmDialog,
 | 
			
		||||
  showFailToast,
 | 
			
		||||
  showNotify,
 | 
			
		||||
  showToast,
 | 
			
		||||
  showDialog,
 | 
			
		||||
  showImagePreview,
 | 
			
		||||
  showSuccessToast
 | 
			
		||||
} from "vant";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import Compressor from "compressorjs";
 | 
			
		||||
import {getSessionId} from "@/store/session";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import {Delete} from "@element-plus/icons-vue";
 | 
			
		||||
 | 
			
		||||
const activeColspan = ref([""])
 | 
			
		||||
 | 
			
		||||
const rates = [
 | 
			
		||||
  {css: "square", value: "1:1", text: "1:1", img: "/images/mj/rate_1_1.png"},
 | 
			
		||||
  {css: "size2-3", value: "2:3", text: "2:3", img: "/images/mj/rate_3_4.png"},
 | 
			
		||||
  {css: "size3-4", value: "3:4", text: "3:4", img: "/images/mj/rate_3_4.png"},
 | 
			
		||||
  {css: "size4-3", value: "4:3", text: "4:3", img: "/images/mj/rate_4_3.png"},
 | 
			
		||||
  {css: "size16-9", value: "16:9", text: "16:9", img: "/images/mj/rate_16_9.png"},
 | 
			
		||||
  {css: "size9-16", value: "9:16", text: "9:16", img: "/images/mj/rate_9_16.png"},
 | 
			
		||||
]
 | 
			
		||||
const models = [
 | 
			
		||||
  {text: "MJ-6.0", value: " --v 6", img: "/images/mj/mj-v6.png"},
 | 
			
		||||
  {text: "MJ-5.2", value: " --v 5.2", img: "/images/mj/mj-v5.2.png"},
 | 
			
		||||
  {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 imgList = ref([])
 | 
			
		||||
const params = ref({
 | 
			
		||||
  task_type: "image",
 | 
			
		||||
  rate: rates[0].value,
 | 
			
		||||
  model: models[0].value,
 | 
			
		||||
  chaos: 0,
 | 
			
		||||
  stylize: 0,
 | 
			
		||||
  seed: 0,
 | 
			
		||||
  img_arr: [],
 | 
			
		||||
  raw: false,
 | 
			
		||||
  iw: 0,
 | 
			
		||||
  prompt: "",
 | 
			
		||||
  neg_prompt: "",
 | 
			
		||||
  tile: false,
 | 
			
		||||
  quality: 0,
 | 
			
		||||
  cref: "",
 | 
			
		||||
  sref: "",
 | 
			
		||||
  cw: 0,
 | 
			
		||||
})
 | 
			
		||||
const userId = ref(0)
 | 
			
		||||
const router = useRouter()
 | 
			
		||||
const runningJobs = ref([])
 | 
			
		||||
const finishedJobs = ref([])
 | 
			
		||||
const socket = ref(null)
 | 
			
		||||
const power = ref(0)
 | 
			
		||||
const activeName = ref("txt2img")
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  checkSession().then(user => {
 | 
			
		||||
    power.value = user['power']
 | 
			
		||||
    userId.value = user.id
 | 
			
		||||
 | 
			
		||||
    fetchRunningJobs()
 | 
			
		||||
    fetchFinishJobs(1)
 | 
			
		||||
    connect()
 | 
			
		||||
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    router.push('/login')
 | 
			
		||||
  });
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  socket.value = null
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const mjPower = ref(1)
 | 
			
		||||
const mjActionPower = ref(1)
 | 
			
		||||
httpGet("/api/config/get?key=system").then(res => {
 | 
			
		||||
  mjPower.value = res.data["mj_power"]
 | 
			
		||||
  mjActionPower.value = res.data["mj_action_power"]
 | 
			
		||||
}).catch(e => {
 | 
			
		||||
  showNotify({type: "danger", message: "获取系统配置失败:" + e.message})
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const heartbeatHandle = ref(null)
 | 
			
		||||
const connect = () => {
 | 
			
		||||
  let host = process.env.VUE_APP_WS_HOST
 | 
			
		||||
  if (host === '') {
 | 
			
		||||
    if (location.protocol === 'https:') {
 | 
			
		||||
      host = 'wss://' + location.host;
 | 
			
		||||
    } else {
 | 
			
		||||
      host = 'ws://' + location.host;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 心跳函数
 | 
			
		||||
  const sendHeartbeat = () => {
 | 
			
		||||
    clearTimeout(heartbeatHandle.value)
 | 
			
		||||
    new Promise((resolve, reject) => {
 | 
			
		||||
      if (socket.value !== null) {
 | 
			
		||||
        socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
 | 
			
		||||
      }
 | 
			
		||||
      resolve("success")
 | 
			
		||||
    }).then(() => {
 | 
			
		||||
      heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const _socket = new WebSocket(host + `/api/mj/client?user_id=${userId.value}`);
 | 
			
		||||
  _socket.addEventListener('open', () => {
 | 
			
		||||
    socket.value = _socket;
 | 
			
		||||
 | 
			
		||||
    // 发送心跳消息
 | 
			
		||||
    sendHeartbeat()
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  _socket.addEventListener('message', event => {
 | 
			
		||||
    if (event.data instanceof Blob) {
 | 
			
		||||
      fetchRunningJobs()
 | 
			
		||||
      fetchFinishJobs(1)
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  _socket.addEventListener('close', () => {
 | 
			
		||||
    if (socket.value !== null) {
 | 
			
		||||
      connect()
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取运行中的任务
 | 
			
		||||
const fetchRunningJobs = (userId) => {
 | 
			
		||||
  httpGet(`/api/mj/jobs?status=0&user_id=${userId}`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    const _jobs = []
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
      if (jobs[i].progress === -1) {
 | 
			
		||||
        showNotify({
 | 
			
		||||
          message: `任务执行失败:${jobs[i]['err_msg']}`,
 | 
			
		||||
          type: 'danger',
 | 
			
		||||
        })
 | 
			
		||||
        if (jobs[i].type === 'image') {
 | 
			
		||||
          power.value += mjPower.value
 | 
			
		||||
        } else {
 | 
			
		||||
          power.value += mjActionPower.value
 | 
			
		||||
        }
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
      _jobs.push(jobs[i])
 | 
			
		||||
    }
 | 
			
		||||
    runningJobs.value = _jobs
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showNotify({type: "danger", message: "获取任务失败:" + e.message})
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const loading = ref(false)
 | 
			
		||||
const finished = ref(false)
 | 
			
		||||
const error = ref(false)
 | 
			
		||||
const page = ref(0)
 | 
			
		||||
const pageSize = ref(10)
 | 
			
		||||
const fetchFinishJobs = (page) => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  // 获取已完成的任务
 | 
			
		||||
  httpGet(`/api/mj/jobs?status=1&page=${page}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
      if (jobs[i].progress === -1) {
 | 
			
		||||
        showNotify({
 | 
			
		||||
          message: `任务ID:${jobs[i]['task_id']} 原因:${jobs[i]['err_msg']}`,
 | 
			
		||||
          type: 'danger',
 | 
			
		||||
        })
 | 
			
		||||
        if (jobs[i].type === 'image') {
 | 
			
		||||
          power.value += mjPower.value
 | 
			
		||||
        } else {
 | 
			
		||||
          power.value += mjActionPower.value
 | 
			
		||||
        }
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (jobs[i]['use_proxy']) {
 | 
			
		||||
        jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?x-oss-process=image/quality,q_60&format=webp'
 | 
			
		||||
      } else {
 | 
			
		||||
        if (jobs[i].type === 'upscale' || jobs[i].type === 'swapFace') {
 | 
			
		||||
          jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/600/q/75'
 | 
			
		||||
        } else {
 | 
			
		||||
          jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/480/q/75'
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (jobs[i].type === 'image' || jobs[i].type === 'variation') {
 | 
			
		||||
        jobs[i]['can_opt'] = true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (jobs.length < pageSize.value) {
 | 
			
		||||
      finished.value = true
 | 
			
		||||
    }
 | 
			
		||||
    if (page === 1) {
 | 
			
		||||
      finishedJobs.value = jobs
 | 
			
		||||
    } else {
 | 
			
		||||
      finishedJobs.value = finishedJobs.value.concat(jobs)
 | 
			
		||||
    }
 | 
			
		||||
    nextTick(() => loading.value = false)
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    loading.value = false
 | 
			
		||||
    error.value = true
 | 
			
		||||
    showFailToast("获取任务失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const onLoad = () => {
 | 
			
		||||
  page.value += 1
 | 
			
		||||
  fetchFinishJobs(page.value)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 切换图片比例
 | 
			
		||||
const changeRate = (item) => {
 | 
			
		||||
  params.value.rate = item.value
 | 
			
		||||
}
 | 
			
		||||
// 切换模型
 | 
			
		||||
const changeModel = (item) => {
 | 
			
		||||
  params.value.model = item.value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const imgKey = ref("")
 | 
			
		||||
const beforeUpload = (key) => {
 | 
			
		||||
  imgKey.value = key
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 图片上传
 | 
			
		||||
const uploadImg = (file) => {
 | 
			
		||||
  file.status = "uploading"
 | 
			
		||||
  // 压缩图片并上传
 | 
			
		||||
  new Compressor(file.file, {
 | 
			
		||||
    quality: 0.6,
 | 
			
		||||
    success(result) {
 | 
			
		||||
      const formData = new FormData();
 | 
			
		||||
      formData.append('file', result, result.name);
 | 
			
		||||
      // 执行上传操作
 | 
			
		||||
      httpPost('/api/upload', formData).then(res => {
 | 
			
		||||
        file.url = res.data.url
 | 
			
		||||
        if (imgKey.value !== "") { // 单张图片上传
 | 
			
		||||
          params.value[imgKey.value] = res.data.url
 | 
			
		||||
          imgKey.value = ''
 | 
			
		||||
        }
 | 
			
		||||
        file.status = "done"
 | 
			
		||||
      }).catch(e => {
 | 
			
		||||
        file.status = 'failed'
 | 
			
		||||
        file.message = '上传失败'
 | 
			
		||||
        showFailToast("图片上传失败:" + e.message)
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    error(err) {
 | 
			
		||||
      console.log(err.message);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const send = (url, index, item) => {
 | 
			
		||||
  httpPost(url, {
 | 
			
		||||
    index: index,
 | 
			
		||||
    channel_id: item.channel_id,
 | 
			
		||||
    message_id: item.message_id,
 | 
			
		||||
    message_hash: item.hash,
 | 
			
		||||
    session_id: getSessionId(),
 | 
			
		||||
    prompt: item.prompt,
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    showSuccessToast("任务推送成功,请耐心等待任务执行...")
 | 
			
		||||
    power.value -= mjActionPower.value
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showFailToast("任务推送失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 图片放大任务
 | 
			
		||||
const upscale = (index, item) => {
 | 
			
		||||
  send('/api/mj/upscale', index, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 图片变换任务
 | 
			
		||||
const variation = (index, item) => {
 | 
			
		||||
  send('/api/mj/variation', index, item)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const generate = () => {
 | 
			
		||||
  if (params.value.prompt === '' && params.value.task_type === "image") {
 | 
			
		||||
    return showFailToast("请输入绘画提示词!")
 | 
			
		||||
  }
 | 
			
		||||
  if (params.value.model.indexOf("niji") !== -1 && params.value.raw) {
 | 
			
		||||
    return showFailToast("动漫模型不允许启用原始模式")
 | 
			
		||||
  }
 | 
			
		||||
  params.value.session_id = getSessionId()
 | 
			
		||||
  params.value.img_arr = imgList.value.map(img => img.url)
 | 
			
		||||
  httpPost("/api/mj/image", params.value).then(() => {
 | 
			
		||||
    showToast("绘画任务推送成功,请耐心等待任务执行")
 | 
			
		||||
    power.value -= mjPower.value
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showFailToast("任务推送失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const removeImage = (item) => {
 | 
			
		||||
  showConfirmDialog({
 | 
			
		||||
    title: '标题',
 | 
			
		||||
    message:
 | 
			
		||||
        '此操作将会删除任务和图片,继续操作码?',
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    httpPost("/api/mj/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
 | 
			
		||||
      showSuccessToast("任务删除成功")
 | 
			
		||||
    }).catch(e => {
 | 
			
		||||
      showFailToast("任务删除失败:" + e.message)
 | 
			
		||||
    })
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    showToast("您取消了操作")
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
// 发布图片到作品墙
 | 
			
		||||
const publishImage = (item, action) => {
 | 
			
		||||
  let text = "图片发布"
 | 
			
		||||
  if (action === false) {
 | 
			
		||||
    text = "取消发布"
 | 
			
		||||
  }
 | 
			
		||||
  httpPost("/api/mj/publish", {id: item.id, action: action}).then(() => {
 | 
			
		||||
    showSuccessToast(text + "成功")
 | 
			
		||||
    item.publish = action
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showFailToast(text + "失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const showPrompt = (item) => {
 | 
			
		||||
  showDialog({
 | 
			
		||||
    title: "绘画提示词",
 | 
			
		||||
    message: item.prompt,
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    // on close
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const imageView = (item) => {
 | 
			
		||||
  showImagePreview([item['img_url']]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 切换菜单
 | 
			
		||||
const tabChange = (tab) => {
 | 
			
		||||
  if (tab === "txt2img" || tab === "img2img") {
 | 
			
		||||
    params.value.task_type = "image"
 | 
			
		||||
  } else {
 | 
			
		||||
    params.value.task_type = tab
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
@import "@/assets/css/mobile/image-mj.styl"
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,523 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="mobile-sd">
 | 
			
		||||
    <van-form @submit="generate">
 | 
			
		||||
      <van-cell-group inset>
 | 
			
		||||
        <div>
 | 
			
		||||
          <van-field
 | 
			
		||||
              v-model="params.sampler"
 | 
			
		||||
              is-link
 | 
			
		||||
              readonly
 | 
			
		||||
              label="采样方法"
 | 
			
		||||
              placeholder="选择采样方法"
 | 
			
		||||
              @click="showSamplerPicker = true"
 | 
			
		||||
          />
 | 
			
		||||
          <van-popup v-model:show="showSamplerPicker" position="bottom" teleport="#app">
 | 
			
		||||
            <van-picker
 | 
			
		||||
                :columns="samplers"
 | 
			
		||||
                @cancel="showSamplerPicker = false"
 | 
			
		||||
                @confirm="samplerConfirm"
 | 
			
		||||
            />
 | 
			
		||||
          </van-popup>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <van-field label="图片尺寸">
 | 
			
		||||
          <template #input>
 | 
			
		||||
            <van-row gutter="20">
 | 
			
		||||
              <van-col span="12">
 | 
			
		||||
                <el-input v-model="params.width" size="small" placeholder="宽"/>
 | 
			
		||||
              </van-col>
 | 
			
		||||
              <van-col span="12">
 | 
			
		||||
                <el-input v-model="params.height" size="small" placeholder="高"/>
 | 
			
		||||
              </van-col>
 | 
			
		||||
            </van-row>
 | 
			
		||||
          </template>
 | 
			
		||||
        </van-field>
 | 
			
		||||
 | 
			
		||||
        <van-field v-model.number="params.steps" label="迭代步数"
 | 
			
		||||
                   placeholder="">
 | 
			
		||||
          <template #right-icon>
 | 
			
		||||
            <van-icon name="info-o"
 | 
			
		||||
                      @click="showInfo('值越大则代表细节越多,同时也意味着出图速度越慢,一般推荐20-30')"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </van-field>
 | 
			
		||||
        <van-field v-model.number="params.cfg_scale" label="引导系数" placeholder="">
 | 
			
		||||
          <template #right-icon>
 | 
			
		||||
            <van-icon name="info-o"
 | 
			
		||||
                      @click="showInfo('提示词引导系数,图像在多大程度上服从提示词,较低值会产生更有创意的结果')"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </van-field>
 | 
			
		||||
        <van-field v-model.number="params.seed" label="随机因子" placeholder="">
 | 
			
		||||
          <template #right-icon>
 | 
			
		||||
            <van-icon name="info-o"
 | 
			
		||||
                      @click="showInfo('随机数种子,相同的种子会得到相同的结果,设置为 -1 则每次随机生成种子')"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </van-field>
 | 
			
		||||
 | 
			
		||||
        <van-field label="高清修复">
 | 
			
		||||
          <template #input>
 | 
			
		||||
            <van-switch v-model="params.hd_fix"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </van-field>
 | 
			
		||||
 | 
			
		||||
        <div v-if="params.hd_fix">
 | 
			
		||||
          <div>
 | 
			
		||||
            <van-field
 | 
			
		||||
                v-model="params.hd_scale_alg"
 | 
			
		||||
                is-link
 | 
			
		||||
                readonly
 | 
			
		||||
                label="放大算法"
 | 
			
		||||
                placeholder="选择放大算法"
 | 
			
		||||
                @click="showUpscalePicker = true"
 | 
			
		||||
            />
 | 
			
		||||
            <van-popup v-model:show="showUpscalePicker" position="bottom" teleport="#app">
 | 
			
		||||
              <van-picker
 | 
			
		||||
                  :columns="upscaleAlgArr"
 | 
			
		||||
                  @cancel="showUpscalePicker = false"
 | 
			
		||||
                  @confirm="upscaleConfirm"
 | 
			
		||||
              />
 | 
			
		||||
            </van-popup>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <van-field v-model.number="params.hd_scale" label="放大倍数"/>
 | 
			
		||||
          <van-field v-model.number="params.hd_steps" label="迭代步数"/>
 | 
			
		||||
 | 
			
		||||
          <van-field label="重绘幅度">
 | 
			
		||||
            <template #input>
 | 
			
		||||
              <van-slider v-model.number="params.hd_redraw_rate" :max="1" :step="0.1"
 | 
			
		||||
                          @update:model-value="showToast('当前值:' + params.hd_redraw_rate)"/>
 | 
			
		||||
            </template>
 | 
			
		||||
            <template #right-icon>
 | 
			
		||||
              <van-icon name="info-o"
 | 
			
		||||
                        @click="showInfo('决定算法对图像内容的影响程度,较大的值将得到越有创意的图像')"/>
 | 
			
		||||
            </template>
 | 
			
		||||
          </van-field>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <van-field
 | 
			
		||||
            v-model="params.prompt"
 | 
			
		||||
            rows="3"
 | 
			
		||||
            autosize
 | 
			
		||||
            type="textarea"
 | 
			
		||||
            placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <van-collapse v-model="activeColspan">
 | 
			
		||||
          <van-collapse-item title="反向提示词" name="neg_prompt">
 | 
			
		||||
            <van-field
 | 
			
		||||
                v-model="params.neg_prompt"
 | 
			
		||||
                rows="3"
 | 
			
		||||
                autosize
 | 
			
		||||
                type="textarea"
 | 
			
		||||
                placeholder="不想出现在图片上的元素(例如:树,建筑)"
 | 
			
		||||
            />
 | 
			
		||||
          </van-collapse-item>
 | 
			
		||||
        </van-collapse>
 | 
			
		||||
 | 
			
		||||
        <div class="text-line pt-6">
 | 
			
		||||
          <el-tag>绘图消耗{{ sdPower }}算力,当前算力:{{ power }}</el-tag>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="text-line">
 | 
			
		||||
          <van-button round block type="primary" native-type="submit">
 | 
			
		||||
            立即生成
 | 
			
		||||
          </van-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </van-cell-group>
 | 
			
		||||
    </van-form>
 | 
			
		||||
 | 
			
		||||
    <h3>任务列表</h3>
 | 
			
		||||
    <div class="running-job-list">
 | 
			
		||||
      <van-empty v-if="runningJobs.length ===0"
 | 
			
		||||
                 image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
 | 
			
		||||
                 image-size="80"
 | 
			
		||||
                 description="暂无记录"
 | 
			
		||||
      />
 | 
			
		||||
      <van-grid :gutter="10" :column-num="3" v-else>
 | 
			
		||||
        <van-grid-item v-for="item in runningJobs">
 | 
			
		||||
          <div v-if="item.progress > 0">
 | 
			
		||||
            <van-image :src="item['img_url']">
 | 
			
		||||
              <template v-slot:error>加载失败</template>
 | 
			
		||||
            </van-image>
 | 
			
		||||
            <div class="progress">
 | 
			
		||||
              <van-circle
 | 
			
		||||
                  v-model:current-rate="item.progress"
 | 
			
		||||
                  :rate="item.progress"
 | 
			
		||||
                  :speed="100"
 | 
			
		||||
                  :text="item.progress+'%'"
 | 
			
		||||
                  :stroke-width="60"
 | 
			
		||||
                  size="90px"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div v-else class="task-in-queue">
 | 
			
		||||
            <span class="icon"><i class="iconfont icon-quick-start"></i></span>
 | 
			
		||||
            <span class="text">排队中</span>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
        </van-grid-item>
 | 
			
		||||
      </van-grid>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <h3>创作记录</h3>
 | 
			
		||||
    <div class="finish-job-list">
 | 
			
		||||
      <van-empty v-if="finishedJobs.length ===0"
 | 
			
		||||
                 image="https://fastly.jsdelivr.net/npm/@vant/assets/custom-empty-image.png"
 | 
			
		||||
                 image-size="80"
 | 
			
		||||
                 description="暂无记录"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <van-list v-else
 | 
			
		||||
                v-model:error="error"
 | 
			
		||||
                v-model:loading="loading"
 | 
			
		||||
                :finished="finished"
 | 
			
		||||
                error-text="请求失败,点击重新加载"
 | 
			
		||||
                finished-text="没有更多了"
 | 
			
		||||
                @load="onLoad"
 | 
			
		||||
      >
 | 
			
		||||
        <van-grid :gutter="10" :column-num="2">
 | 
			
		||||
          <van-grid-item v-for="item in finishedJobs">
 | 
			
		||||
            <div class="job-item">
 | 
			
		||||
              <van-image
 | 
			
		||||
                  :src="item['img_url']"
 | 
			
		||||
                  :class="item['can_opt'] ? '' : 'upscale'"
 | 
			
		||||
                  lazy-load
 | 
			
		||||
                  @click="imageView(item)"
 | 
			
		||||
                  fit="cover">
 | 
			
		||||
                <template v-slot:loading>
 | 
			
		||||
                  <van-loading type="spinner" size="20"/>
 | 
			
		||||
                </template>
 | 
			
		||||
              </van-image>
 | 
			
		||||
 | 
			
		||||
              <div class="remove">
 | 
			
		||||
                <el-button type="danger" :icon="Delete" @click="removeImage($event, item)" circle/>
 | 
			
		||||
                <el-button type="warning" v-if="item.publish" @click="publishImage($event,item, false)"
 | 
			
		||||
                           circle>
 | 
			
		||||
                  <i class="iconfont icon-cancel-share"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
                <el-button type="success" v-else @click="publishImage($event, item, true)" circle>
 | 
			
		||||
                  <i class="iconfont icon-share-bold"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
                <el-button type="primary" @click="showTask(item)" circle>
 | 
			
		||||
                  <i class="iconfont icon-prompt"></i>
 | 
			
		||||
                </el-button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </van-grid-item>
 | 
			
		||||
        </van-grid>
 | 
			
		||||
      </van-list>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {onMounted, onUnmounted, ref} from "vue"
 | 
			
		||||
import {Delete} from "@element-plus/icons-vue";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import Clipboard from "clipboard";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import {getSessionId} from "@/store/session";
 | 
			
		||||
import {
 | 
			
		||||
  showConfirmDialog, showDialog,
 | 
			
		||||
  showFailToast,
 | 
			
		||||
  showImagePreview,
 | 
			
		||||
  showNotify,
 | 
			
		||||
  showSuccessToast,
 | 
			
		||||
  showToast
 | 
			
		||||
} from "vant";
 | 
			
		||||
 | 
			
		||||
const listBoxHeight = ref(window.innerHeight - 40)
 | 
			
		||||
const mjBoxHeight = ref(window.innerHeight - 150)
 | 
			
		||||
const showTaskDialog = ref(false)
 | 
			
		||||
const item = ref({})
 | 
			
		||||
const showLoginDialog = ref(false)
 | 
			
		||||
const isLogin = ref(false)
 | 
			
		||||
const activeColspan = ref([""])
 | 
			
		||||
 | 
			
		||||
window.onresize = () => {
 | 
			
		||||
  listBoxHeight.value = window.innerHeight - 40
 | 
			
		||||
  mjBoxHeight.value = window.innerHeight - 150
 | 
			
		||||
}
 | 
			
		||||
const samplers = ref([
 | 
			
		||||
  {text: "Euler a", value: "Euler a"},
 | 
			
		||||
  {text: "DPM++ 2S a Karras", value: "DPM++ 2S a Karras"},
 | 
			
		||||
  {text: "DPM++ 2M Karras", value: "DPM++ 2M Karras"},
 | 
			
		||||
  {text: "DPM++ 2M SDE Karras", value: "DPM++ 2M SDE Karras"},
 | 
			
		||||
  {text: "DPM++ 2M Karras", value: "DPM++ 2M Karras"},
 | 
			
		||||
  {text: "DPM++ 3M SDE Karras", value: "DPM++ 3M SDE Karras"},
 | 
			
		||||
])
 | 
			
		||||
const showSamplerPicker = ref(false)
 | 
			
		||||
 | 
			
		||||
const upscaleAlgArr = ref([
 | 
			
		||||
  {text: "Latent", value: "Latent"},
 | 
			
		||||
  {text: "ESRGAN_4x", value: "ESRGAN_4x"},
 | 
			
		||||
  {text: "ESRGAN 4x+", value: "ESRGAN 4x+"},
 | 
			
		||||
  {text: "SwinIR_4x", value: "SwinIR_4x"},
 | 
			
		||||
  {text: "LDSR", value: "LDSR"},
 | 
			
		||||
])
 | 
			
		||||
const showUpscalePicker = ref(false)
 | 
			
		||||
 | 
			
		||||
const params = ref({
 | 
			
		||||
  width: 1024,
 | 
			
		||||
  height: 1024,
 | 
			
		||||
  sampler: samplers.value[0].value,
 | 
			
		||||
  seed: -1,
 | 
			
		||||
  steps: 20,
 | 
			
		||||
  cfg_scale: 7,
 | 
			
		||||
  hd_fix: false,
 | 
			
		||||
  hd_redraw_rate: 0.7,
 | 
			
		||||
  hd_scale: 2,
 | 
			
		||||
  hd_scale_alg: upscaleAlgArr.value[0].value,
 | 
			
		||||
  hd_steps: 0,
 | 
			
		||||
  prompt: "",
 | 
			
		||||
  neg_prompt: "nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet",
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const runningJobs = ref([])
 | 
			
		||||
const finishedJobs = ref([])
 | 
			
		||||
const router = useRouter()
 | 
			
		||||
// 检查是否有画同款的参数
 | 
			
		||||
const _params = router.currentRoute.value.params["copyParams"]
 | 
			
		||||
if (_params) {
 | 
			
		||||
  params.value = JSON.parse(_params)
 | 
			
		||||
}
 | 
			
		||||
const power = ref(0)
 | 
			
		||||
const sdPower = ref(0) // 画一张 SD 图片消耗算力
 | 
			
		||||
 | 
			
		||||
const socket = ref(null)
 | 
			
		||||
const userId = ref(0)
 | 
			
		||||
const heartbeatHandle = ref(null)
 | 
			
		||||
const connect = () => {
 | 
			
		||||
  let host = process.env.VUE_APP_WS_HOST
 | 
			
		||||
  if (host === '') {
 | 
			
		||||
    if (location.protocol === 'https:') {
 | 
			
		||||
      host = 'wss://' + location.host;
 | 
			
		||||
    } else {
 | 
			
		||||
      host = 'ws://' + location.host;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 心跳函数
 | 
			
		||||
  const sendHeartbeat = () => {
 | 
			
		||||
    clearTimeout(heartbeatHandle.value)
 | 
			
		||||
    new Promise((resolve, reject) => {
 | 
			
		||||
      if (socket.value !== null) {
 | 
			
		||||
        socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
 | 
			
		||||
      }
 | 
			
		||||
      resolve("success")
 | 
			
		||||
    }).then(() => {
 | 
			
		||||
      heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const _socket = new WebSocket(host + `/api/sd/client?user_id=${userId.value}`);
 | 
			
		||||
  _socket.addEventListener('open', () => {
 | 
			
		||||
    socket.value = _socket;
 | 
			
		||||
 | 
			
		||||
    // 发送心跳消息
 | 
			
		||||
    sendHeartbeat()
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  _socket.addEventListener('message', event => {
 | 
			
		||||
    if (event.data instanceof Blob) {
 | 
			
		||||
      fetchRunningJobs()
 | 
			
		||||
      finished.value = false
 | 
			
		||||
      page.value = 1
 | 
			
		||||
      fetchFinishJobs(page.value)
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  _socket.addEventListener('close', () => {
 | 
			
		||||
    if (socket.value !== null) {
 | 
			
		||||
      connect()
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const clipboard = ref(null)
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  initData()
 | 
			
		||||
  clipboard.value = new Clipboard('.copy-prompt-sd');
 | 
			
		||||
  clipboard.value.on('success', () => {
 | 
			
		||||
    showNotify({type: "success", message: "复制成功!"});
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  clipboard.value.on('error', () => {
 | 
			
		||||
    showNotify({type: "danger", message: '复制失败!'});
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  httpGet("/api/config/get?key=system").then(res => {
 | 
			
		||||
    sdPower.value = res.data["sd_power"]
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showNotify({type: "danger", message: "获取系统配置失败:" + e.message})
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  clipboard.value.destroy()
 | 
			
		||||
  socket.value = null
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const initData = () => {
 | 
			
		||||
  checkSession().then(user => {
 | 
			
		||||
    power.value = user['power']
 | 
			
		||||
    userId.value = user.id
 | 
			
		||||
    isLogin.value = true
 | 
			
		||||
    fetchRunningJobs()
 | 
			
		||||
    fetchFinishJobs(1)
 | 
			
		||||
    connect()
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    loading.value = false
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const fetchRunningJobs = () => {
 | 
			
		||||
  // 获取运行中的任务
 | 
			
		||||
  httpGet(`/api/sd/jobs?status=0`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    const _jobs = []
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
      if (jobs[i].progress === -1) {
 | 
			
		||||
        showNotify({
 | 
			
		||||
          message: `任务ID:${jobs[i]['task_id']} 原因:${jobs[i]['err_msg']}`,
 | 
			
		||||
          type: 'danger',
 | 
			
		||||
        })
 | 
			
		||||
        power.value += sdPower.value
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
      _jobs.push(jobs[i])
 | 
			
		||||
    }
 | 
			
		||||
    runningJobs.value = _jobs
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showNotify({type: "danger", message: "获取任务失败:" + e.message})
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const loading = ref(false)
 | 
			
		||||
const finished = ref(false)
 | 
			
		||||
const error = ref(false)
 | 
			
		||||
const page = ref(0)
 | 
			
		||||
const pageSize = ref(10)
 | 
			
		||||
// 获取已完成的任务
 | 
			
		||||
const fetchFinishJobs = (page) => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  httpGet(`/api/sd/jobs?status=1&page=${page}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
    if (res.data.length < pageSize.value) {
 | 
			
		||||
      finished.value = true
 | 
			
		||||
    }
 | 
			
		||||
    if (page === 1) {
 | 
			
		||||
      finishedJobs.value = res.data
 | 
			
		||||
    } else {
 | 
			
		||||
      finishedJobs.value = finishedJobs.value.concat(res.data)
 | 
			
		||||
    }
 | 
			
		||||
    loading.value = false
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    loading.value = false
 | 
			
		||||
    showNotify({type: "danger", message: "获取任务失败:" + e.message})
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const onLoad = () => {
 | 
			
		||||
  page.value += 1
 | 
			
		||||
  fetchFinishJobs(page.value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 创建绘图任务
 | 
			
		||||
const promptRef = ref(null)
 | 
			
		||||
const generate = () => {
 | 
			
		||||
  if (params.value.prompt === '') {
 | 
			
		||||
    promptRef.value.focus()
 | 
			
		||||
    return showToast("请输入绘画提示词!")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!isLogin.value) {
 | 
			
		||||
    showLoginDialog.value = true
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (params.value.seed === '') {
 | 
			
		||||
    params.value.seed = -1
 | 
			
		||||
  }
 | 
			
		||||
  params.value.session_id = getSessionId()
 | 
			
		||||
  httpPost("/api/sd/image", params.value).then(() => {
 | 
			
		||||
    showSuccessToast("绘画任务推送成功,请耐心等待任务执行...")
 | 
			
		||||
    power.value -= sdPower.value
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showFailToast("任务推送失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const showTask = (row) => {
 | 
			
		||||
  item.value = row
 | 
			
		||||
  showTaskDialog.value = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const copyParams = (row) => {
 | 
			
		||||
  params.value = row.params
 | 
			
		||||
  showTaskDialog.value = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const removeImage = (event, item) => {
 | 
			
		||||
  event.stopPropagation()
 | 
			
		||||
  showConfirmDialog({
 | 
			
		||||
    title: '标题',
 | 
			
		||||
    message:
 | 
			
		||||
        '此操作将会删除任务和图片,继续操作码?',
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    httpPost("/api/sd/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
 | 
			
		||||
      showSuccessToast("任务删除成功")
 | 
			
		||||
    }).catch(e => {
 | 
			
		||||
      showFailToast("任务删除失败:" + e.message)
 | 
			
		||||
    })
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    showToast("您取消了操作")
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 发布图片到作品墙
 | 
			
		||||
const publishImage = (event, item, action) => {
 | 
			
		||||
  event.stopPropagation()
 | 
			
		||||
  let text = "图片发布"
 | 
			
		||||
  if (action === false) {
 | 
			
		||||
    text = "取消发布"
 | 
			
		||||
  }
 | 
			
		||||
  httpPost("/api/sd/publish", {id: item.id, action: action}).then(() => {
 | 
			
		||||
    showSuccessToast(text + "成功")
 | 
			
		||||
    item.publish = action
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showFailToast(text + "失败:" + e.message)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const imageView = (item) => {
 | 
			
		||||
  showImagePreview([item['img_url']]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const samplerConfirm = (item) => {
 | 
			
		||||
  params.value.sampler = item.selectedOptions[0].text;
 | 
			
		||||
  showSamplerPicker.value = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const upscaleConfirm = (item) => {
 | 
			
		||||
  params.value.hd_scale_alg = item.selectedOptions[0].text;
 | 
			
		||||
  showUpscalePicker.value = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const showInfo = (message) => {
 | 
			
		||||
  showDialog({
 | 
			
		||||
    title: "参数说明",
 | 
			
		||||
    message: message,
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    // on close
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
@import "@/assets/css/mobile/image-sd.styl"
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,133 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="img-wall container">
 | 
			
		||||
    <van-nav-bar :title="title"/>
 | 
			
		||||
 | 
			
		||||
    <div class="content">
 | 
			
		||||
      <van-tabs v-model:active="activeName">
 | 
			
		||||
        <van-tab title="MidJourney" name="mj">
 | 
			
		||||
          <van-list
 | 
			
		||||
              v-model:error="data['mj'].error"
 | 
			
		||||
              v-model:loading="data['mj'].loading"
 | 
			
		||||
              :finished="data['mj'].finished"
 | 
			
		||||
              error-text="请求失败,点击重新加载"
 | 
			
		||||
              finished-text="没有更多了"
 | 
			
		||||
              @load="onLoad"
 | 
			
		||||
              style="height: 100%;width: 100%;"
 | 
			
		||||
          >
 | 
			
		||||
            <van-cell v-for="item in data['mj'].data" :key="item.id">
 | 
			
		||||
              <van-image :src="item['img_thumb']" @click="showPrompt(item)" fit="cover"/>
 | 
			
		||||
            </van-cell>
 | 
			
		||||
          </van-list>
 | 
			
		||||
        </van-tab>
 | 
			
		||||
        <van-tab title="StableDiffusion" name="sd">
 | 
			
		||||
          <van-list
 | 
			
		||||
              v-model:error="data['sd'].error"
 | 
			
		||||
              v-model:loading="data['sd'].loading"
 | 
			
		||||
              :finished="data['sd'].finished"
 | 
			
		||||
              error-text="请求失败,点击重新加载"
 | 
			
		||||
              finished-text="没有更多了"
 | 
			
		||||
              @load="onLoad"
 | 
			
		||||
          >
 | 
			
		||||
            <van-cell v-for="item in data['sd'].data" :key="item.id">
 | 
			
		||||
              <van-image :src="item['img_thumb']" @click="showPrompt(item)" fit="cover"/>
 | 
			
		||||
            </van-cell>
 | 
			
		||||
          </van-list>
 | 
			
		||||
        </van-tab>
 | 
			
		||||
        <van-tab title="DALLE3" name="dalle3">
 | 
			
		||||
          <van-empty description="功能正在开发中"/>
 | 
			
		||||
        </van-tab>
 | 
			
		||||
      </van-tabs>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {onMounted, ref} from "vue";
 | 
			
		||||
import {httpGet, httpPost} from "@/utils/http";
 | 
			
		||||
import {showDialog, showFailToast, showSuccessToast} from "vant";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
 | 
			
		||||
const title = ref('图片创作广场')
 | 
			
		||||
const activeName = ref("mj")
 | 
			
		||||
const data = ref({
 | 
			
		||||
  "mj": {
 | 
			
		||||
    loading: false,
 | 
			
		||||
    finished: false,
 | 
			
		||||
    error: false,
 | 
			
		||||
    page: 1,
 | 
			
		||||
    pageSize: 12,
 | 
			
		||||
    url: "/api/mj/jobs",
 | 
			
		||||
    data: []
 | 
			
		||||
  },
 | 
			
		||||
  "sd": {
 | 
			
		||||
    loading: false,
 | 
			
		||||
    finished: false,
 | 
			
		||||
    error: false,
 | 
			
		||||
    page: 1,
 | 
			
		||||
    pageSize: 12,
 | 
			
		||||
    url: "/api/sd/jobs",
 | 
			
		||||
    data: []
 | 
			
		||||
  },
 | 
			
		||||
  "dalle3": {
 | 
			
		||||
    loading: false,
 | 
			
		||||
    finished: false,
 | 
			
		||||
    error: false,
 | 
			
		||||
    page: 1,
 | 
			
		||||
    pageSize: 12,
 | 
			
		||||
    url: "/api/dalle3/jobs",
 | 
			
		||||
    data: []
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const onLoad = () => {
 | 
			
		||||
  const d = data.value[activeName.value]
 | 
			
		||||
  httpGet(`${d.url}?status=1&page=${d.page}&page_size=${d.pageSize}&publish=true`).then(res => {
 | 
			
		||||
    d.loading = false
 | 
			
		||||
    if (res.data.length === 0) {
 | 
			
		||||
      d.finished = true
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 生成缩略图
 | 
			
		||||
    const imageList = res.data
 | 
			
		||||
    for (let i = 0; i < imageList.length; i++) {
 | 
			
		||||
      imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75"
 | 
			
		||||
    }
 | 
			
		||||
    if (imageList.length < d.pageSize) {
 | 
			
		||||
      d.finished = true
 | 
			
		||||
    }
 | 
			
		||||
    if (d.data.length === 0) {
 | 
			
		||||
      d.data = imageList
 | 
			
		||||
    } else {
 | 
			
		||||
      d.data = d.data.concat(imageList)
 | 
			
		||||
    }
 | 
			
		||||
    d.page += 1
 | 
			
		||||
  }).catch(() => {
 | 
			
		||||
    d.error = true
 | 
			
		||||
    showFailToast("加载图片数据失败")
 | 
			
		||||
  })
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const showPrompt = (item) => {
 | 
			
		||||
  showDialog({
 | 
			
		||||
    title: "绘画提示词",
 | 
			
		||||
    message: item.prompt,
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    // on close
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
.img-wall {
 | 
			
		||||
  .content {
 | 
			
		||||
    padding-top 60px
 | 
			
		||||
 | 
			
		||||
    .van-cell__value {
 | 
			
		||||
      .van-image {
 | 
			
		||||
        width 100%
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -302,7 +302,7 @@ const pay = (payWay, item) => {
 | 
			
		||||
    if (isWeChatBrowser() && payWay === 'wechat') {
 | 
			
		||||
      showFailToast("请在系统自带浏览器打开支付页面,或者在 PC 端进行扫码支付")
 | 
			
		||||
    } else {
 | 
			
		||||
      location.href = res.data
 | 
			
		||||
      location.href = res.data.url
 | 
			
		||||
    }
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
    showFailToast("生成支付订单失败:" + e.message)
 | 
			
		||||
 
 | 
			
		||||
@@ -317,7 +317,7 @@ const initData = () => {
 | 
			
		||||
 | 
			
		||||
const fetchRunningJobs = () => {
 | 
			
		||||
  // 获取运行中的任务
 | 
			
		||||
  httpGet(`/api/dall/jobs?status=0`).then(res => {
 | 
			
		||||
  httpGet(`/api/dall/jobs?finish=0`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    const _jobs = []
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
@@ -345,7 +345,7 @@ const pageSize = ref(10)
 | 
			
		||||
// 获取已完成的任务
 | 
			
		||||
const fetchFinishJobs = (page) => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  httpGet(`/api/dall/jobs?status=1&page=${page}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
  httpGet(`/api/dall/jobs?finish=1&page=${page}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
    if (res.data.length < pageSize.value) {
 | 
			
		||||
      finished.value = true
 | 
			
		||||
    }
 | 
			
		||||
@@ -410,7 +410,7 @@ const removeImage = (event, item) => {
 | 
			
		||||
    message:
 | 
			
		||||
        '此操作将会删除任务和图片,继续操作码?',
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    httpPost("/api/dall/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
 | 
			
		||||
    httpGet("/api/dall/remove", {id: item.id, user_id: item.user_id}).then(() => {
 | 
			
		||||
      showSuccessToast("任务删除成功")
 | 
			
		||||
    }).catch(e => {
 | 
			
		||||
      showFailToast("任务删除失败:" + e.message)
 | 
			
		||||
@@ -427,7 +427,7 @@ const publishImage = (event, item, action) => {
 | 
			
		||||
  if (action === false) {
 | 
			
		||||
    text = "取消发布"
 | 
			
		||||
  }
 | 
			
		||||
  httpPost("/api/dall/publish", {id: item.id, action: action}).then(() => {
 | 
			
		||||
  httpGet("/api/dall/publish", {id: item.id, action: action, user_id: item.user_id}).then(() => {
 | 
			
		||||
    showSuccessToast(text + "成功")
 | 
			
		||||
    item.publish = action
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
 
 | 
			
		||||
@@ -421,7 +421,7 @@ const connect = () => {
 | 
			
		||||
 | 
			
		||||
// 获取运行中的任务
 | 
			
		||||
const fetchRunningJobs = (userId) => {
 | 
			
		||||
  httpGet(`/api/mj/jobs?status=0&user_id=${userId}`).then(res => {
 | 
			
		||||
  httpGet(`/api/mj/jobs?finish=0&user_id=${userId}`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    const _jobs = []
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
@@ -453,7 +453,7 @@ const pageSize = ref(10)
 | 
			
		||||
const fetchFinishJobs = (page) => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  // 获取已完成的任务
 | 
			
		||||
  httpGet(`/api/mj/jobs?status=1&page=${page}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
  httpGet(`/api/mj/jobs?finish=1&page=${page}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
      if (jobs[i].progress === -1) {
 | 
			
		||||
@@ -600,7 +600,7 @@ const removeImage = (item) => {
 | 
			
		||||
    message:
 | 
			
		||||
        '此操作将会删除任务和图片,继续操作码?',
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    httpPost("/api/mj/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
 | 
			
		||||
    httpGet("/api/mj/remove", {id: item.id, user_id: item.user_id}).then(() => {
 | 
			
		||||
      showSuccessToast("任务删除成功")
 | 
			
		||||
    }).catch(e => {
 | 
			
		||||
      showFailToast("任务删除失败:" + e.message)
 | 
			
		||||
@@ -615,7 +615,7 @@ const publishImage = (item, action) => {
 | 
			
		||||
  if (action === false) {
 | 
			
		||||
    text = "取消发布"
 | 
			
		||||
  }
 | 
			
		||||
  httpPost("/api/mj/publish", {id: item.id, action: action}).then(() => {
 | 
			
		||||
  httpGet("/api/mj/publish", {id: item.id, action: action,user_id: item.user_id}).then(() => {
 | 
			
		||||
    showSuccessToast(text + "成功")
 | 
			
		||||
    item.publish = action
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
 
 | 
			
		||||
@@ -381,7 +381,7 @@ const initData = () => {
 | 
			
		||||
 | 
			
		||||
const fetchRunningJobs = () => {
 | 
			
		||||
  // 获取运行中的任务
 | 
			
		||||
  httpGet(`/api/sd/jobs?status=0`).then(res => {
 | 
			
		||||
  httpGet(`/api/sd/jobs?finish=0`).then(res => {
 | 
			
		||||
    const jobs = res.data
 | 
			
		||||
    const _jobs = []
 | 
			
		||||
    for (let i = 0; i < jobs.length; i++) {
 | 
			
		||||
@@ -409,7 +409,7 @@ const pageSize = ref(10)
 | 
			
		||||
// 获取已完成的任务
 | 
			
		||||
const fetchFinishJobs = (page) => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  httpGet(`/api/sd/jobs?status=1&page=${page}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
  httpGet(`/api/sd/jobs?finish=1&page=${page}&page_size=${pageSize.value}`).then(res => {
 | 
			
		||||
    if (res.data.length < pageSize.value) {
 | 
			
		||||
      finished.value = true
 | 
			
		||||
    }
 | 
			
		||||
@@ -474,7 +474,7 @@ const removeImage = (event, item) => {
 | 
			
		||||
    message:
 | 
			
		||||
        '此操作将会删除任务和图片,继续操作码?',
 | 
			
		||||
  }).then(() => {
 | 
			
		||||
    httpPost("/api/sd/remove", {id: item.id, img_url: item.img_url, user_id: userId.value}).then(() => {
 | 
			
		||||
    httpGet("/api/sd/remove", {id: item.id, user_id: item.user}).then(() => {
 | 
			
		||||
      showSuccessToast("任务删除成功")
 | 
			
		||||
    }).catch(e => {
 | 
			
		||||
      showFailToast("任务删除失败:" + e.message)
 | 
			
		||||
@@ -491,7 +491,7 @@ const publishImage = (event, item, action) => {
 | 
			
		||||
  if (action === false) {
 | 
			
		||||
    text = "取消发布"
 | 
			
		||||
  }
 | 
			
		||||
  httpPost("/api/sd/publish", {id: item.id, action: action}).then(() => {
 | 
			
		||||
  httpGet("/api/sd/publish", {id: item.id, action: action, user_id: item.user}).then(() => {
 | 
			
		||||
    showSuccessToast(text + "成功")
 | 
			
		||||
    item.publish = action
 | 
			
		||||
  }).catch(e => {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user