mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	feat: implements image function replace Mj with DALL-E-3
This commit is contained in:
		@@ -148,6 +148,7 @@ func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc {
 | 
			
		||||
			c.Request.URL.Path == "/api/chat/detail" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/role/list" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/mj/jobs" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/invite/hits" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/sd/jobs" ||
 | 
			
		||||
			strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
 | 
			
		||||
			strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") ||
 | 
			
		||||
 
 | 
			
		||||
@@ -145,7 +145,6 @@ type SystemConfig struct {
 | 
			
		||||
	VipMonthCalls    int      `json:"vip_month_calls"`     // 会员每个赠送的调用次数
 | 
			
		||||
	EnabledRegister  bool     `json:"enabled_register"`    // 是否启用注册功能,关闭注册功能之后将无法注册
 | 
			
		||||
	EnabledMsg       bool     `json:"enabled_msg"`         // 是否启用短信验证码服务
 | 
			
		||||
	EnabledDraw      bool     `json:"enabled_draw"`        // 是否启用 AI 绘画功能
 | 
			
		||||
	RewardImg        string   `json:"reward_img"`          // 众筹收款二维码地址
 | 
			
		||||
	EnabledFunction  bool     `json:"enabled_function"`    // 启用 API 函数功能
 | 
			
		||||
	EnabledReward    bool     `json:"enabled_reward"`      // 启用众筹功能
 | 
			
		||||
 
 | 
			
		||||
@@ -23,10 +23,10 @@ type Property struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FuncZaoBao     = "zao_bao"     // 每日早报
 | 
			
		||||
	FuncHeadLine   = "headline"    // 今日头条
 | 
			
		||||
	FuncWeibo      = "weibo_hot"   // 微博热搜
 | 
			
		||||
	FuncMidJourney = "mid_journey" // MJ 绘画
 | 
			
		||||
	FuncZaoBao   = "zao_bao"    // 每日早报
 | 
			
		||||
	FuncHeadLine = "headline"   // 今日头条
 | 
			
		||||
	FuncWeibo    = "weibo_hot"  // 微博热搜
 | 
			
		||||
	FuncImage    = "draw_image" // AI 绘画
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var InnerFunctions = []Function{
 | 
			
		||||
@@ -76,14 +76,14 @@ var InnerFunctions = []Function{
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		Name:        FuncMidJourney,
 | 
			
		||||
		Description: "AI 绘画工具,使用 MJ MidJourney API 进行 AI 绘画",
 | 
			
		||||
		Name:        FuncImage,
 | 
			
		||||
		Description: "AI 绘画工具,根据输入的绘图描述用 AI 工具进行绘画",
 | 
			
		||||
		Parameters: Parameters{
 | 
			
		||||
			Type: "object",
 | 
			
		||||
			Properties: map[string]Property{
 | 
			
		||||
				"prompt": {
 | 
			
		||||
					Type:        "string",
 | 
			
		||||
					Description: "提示词,如果该参数中有中文的话,则需要翻译成英文。提示词中的参数作为提示的一部分,不要删除",
 | 
			
		||||
					Description: "提示词,如果该参数中有中文的话,则需要翻译成英文。",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			Required: []string{},
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ func (h *ApiKeyHandler) Save(c *gin.Context) {
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Id       uint   `json:"id"`
 | 
			
		||||
		Platform string `json:"platform"`
 | 
			
		||||
		Type     string `json:"type"`
 | 
			
		||||
		Value    string `json:"value"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
@@ -40,7 +41,8 @@ func (h *ApiKeyHandler) Save(c *gin.Context) {
 | 
			
		||||
	}
 | 
			
		||||
	apiKey.Platform = data.Platform
 | 
			
		||||
	apiKey.Value = data.Value
 | 
			
		||||
	res := h.db.Debug().Save(&apiKey)
 | 
			
		||||
	apiKey.Type = data.Type
 | 
			
		||||
	res := h.db.Save(&apiKey)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "更新数据库失败!")
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -127,12 +127,12 @@ func (h *ChatHandler) sendAzureMessage(
 | 
			
		||||
			logger.Debugf("函数名称: %s, 函数参数:%s", functionName, params)
 | 
			
		||||
 | 
			
		||||
			// for creating image, check if the user's img_calls > 0
 | 
			
		||||
			if functionName == types.FuncMidJourney && userVo.ImgCalls <= 0 {
 | 
			
		||||
			if functionName == types.FuncImage && userVo.ImgCalls <= 0 {
 | 
			
		||||
				utils.ReplyMessage(ws, "**当前用户剩余绘图次数已用尽,请扫描下面二维码联系管理员!**")
 | 
			
		||||
				utils.ReplyMessage(ws, ErrImg)
 | 
			
		||||
			} else {
 | 
			
		||||
				f := h.App.Functions[functionName]
 | 
			
		||||
				if functionName == types.FuncMidJourney {
 | 
			
		||||
				if functionName == types.FuncImage {
 | 
			
		||||
					params["user_id"] = userVo.Id
 | 
			
		||||
					params["role_id"] = role.Id
 | 
			
		||||
					params["chat_id"] = session.ChatId
 | 
			
		||||
@@ -149,9 +149,8 @@ func (h *ChatHandler) sendAzureMessage(
 | 
			
		||||
					contents = append(contents, msg)
 | 
			
		||||
				} else {
 | 
			
		||||
					content := data
 | 
			
		||||
					if functionName == types.FuncMidJourney {
 | 
			
		||||
						content = fmt.Sprintf("绘画提示词:%s 已推送任务到 MidJourney 机器人,请耐心等待任务执行...", data)
 | 
			
		||||
						h.mjService.ChatClients.Put(session.SessionId, ws)
 | 
			
		||||
					if functionName == types.FuncImage {
 | 
			
		||||
						content = fmt.Sprintf("下面是根据您的描述创作的图片,他们描绘了 【%s】 的场景", params["prompt"])
 | 
			
		||||
						// update user's img_calls
 | 
			
		||||
						h.db.Model(&model.User{}).Where("id = ?", userVo.Id).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
 | 
			
		||||
					}
 | 
			
		||||
 
 | 
			
		||||
@@ -224,9 +224,6 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio
 | 
			
		||||
		if h.App.SysConfig.EnabledFunction {
 | 
			
		||||
			var functions = make([]types.Function, 0)
 | 
			
		||||
			for _, f := range types.InnerFunctions {
 | 
			
		||||
				if !h.App.SysConfig.EnabledDraw && f.Name == types.FuncMidJourney {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				functions = append(functions, f)
 | 
			
		||||
			}
 | 
			
		||||
			req.Functions = functions
 | 
			
		||||
@@ -405,7 +402,7 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
 | 
			
		||||
	}
 | 
			
		||||
	if *apiKey == "" {
 | 
			
		||||
		var key model.ApiKey
 | 
			
		||||
		res := h.db.Where("platform = ?", platform).Order("last_used_at ASC").First(&key)
 | 
			
		||||
		res := h.db.Where("platform = ? AND type = ?", platform, "chat").Order("last_used_at ASC").First(&key)
 | 
			
		||||
		if res.Error != nil {
 | 
			
		||||
			return nil, errors.New("no available key, please import key")
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -126,12 +126,12 @@ func (h *ChatHandler) sendOpenAiMessage(
 | 
			
		||||
			logger.Debugf("函数名称: %s, 函数参数:%s", functionName, params)
 | 
			
		||||
 | 
			
		||||
			// for creating image, check if the user's img_calls > 0
 | 
			
		||||
			if functionName == types.FuncMidJourney && userVo.ImgCalls <= 0 {
 | 
			
		||||
			if functionName == types.FuncImage && userVo.ImgCalls <= 0 {
 | 
			
		||||
				utils.ReplyMessage(ws, "**当前用户剩余绘图次数已用尽,请扫描下面二维码联系管理员!**")
 | 
			
		||||
				utils.ReplyMessage(ws, ErrImg)
 | 
			
		||||
			} else {
 | 
			
		||||
				f := h.App.Functions[functionName]
 | 
			
		||||
				if functionName == types.FuncMidJourney {
 | 
			
		||||
				if functionName == types.FuncImage {
 | 
			
		||||
					params["user_id"] = userVo.Id
 | 
			
		||||
					params["role_id"] = role.Id
 | 
			
		||||
					params["chat_id"] = session.ChatId
 | 
			
		||||
@@ -148,9 +148,8 @@ func (h *ChatHandler) sendOpenAiMessage(
 | 
			
		||||
					contents = append(contents, msg)
 | 
			
		||||
				} else {
 | 
			
		||||
					content := data
 | 
			
		||||
					if functionName == types.FuncMidJourney {
 | 
			
		||||
						content = fmt.Sprintf("绘画提示词:%s 已推送任务到 MidJourney 机器人,请耐心等待任务执行...", data)
 | 
			
		||||
						h.mjService.ChatClients.Put(session.SessionId, ws)
 | 
			
		||||
					if functionName == types.FuncImage {
 | 
			
		||||
						content = fmt.Sprintf("下面是根据您的描述创作的图片,他们描绘了 【%s】 的场景。%s", params["prompt"], data)
 | 
			
		||||
						// update user's img_calls
 | 
			
		||||
						h.db.Model(&model.User{}).Where("id = ?", userVo.Id).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
 | 
			
		||||
					}
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ func (h *ChatHandler) sendXunFeiMessage(
 | 
			
		||||
	var apiKey = userVo.ChatConfig.ApiKeys[session.Model.Platform]
 | 
			
		||||
	if apiKey == "" {
 | 
			
		||||
		var key model.ApiKey
 | 
			
		||||
		res := h.db.Where("platform = ?", session.Model.Platform).Order("last_used_at ASC").First(&key)
 | 
			
		||||
		res := h.db.Where("platform = ? AND type = ?", session.Model.Platform, "chat").Order("last_used_at ASC").First(&key)
 | 
			
		||||
		if res.Error != nil {
 | 
			
		||||
			utils.ReplyMessage(ws, "抱歉😔😔😔,系统已经没有可用的 API KEY,请联系管理员!")
 | 
			
		||||
			return nil
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										92
									
								
								api/service/fun/func_img.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								api/service/fun/func_img.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
package fun
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/service/oss"
 | 
			
		||||
	"chatplus/store/model"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/imroc/req/v3"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AI 绘画函数
 | 
			
		||||
 | 
			
		||||
type FuncImage struct {
 | 
			
		||||
	name          string
 | 
			
		||||
	apiURL        string
 | 
			
		||||
	db            *gorm.DB
 | 
			
		||||
	uploadManager *oss.UploaderManager
 | 
			
		||||
	proxyURL      string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewImageFunc(db *gorm.DB, manager *oss.UploaderManager, config *types.AppConfig) FuncImage {
 | 
			
		||||
	return FuncImage{
 | 
			
		||||
		db:            db,
 | 
			
		||||
		name:          "DALL-E3 绘画",
 | 
			
		||||
		uploadManager: manager,
 | 
			
		||||
		proxyURL:      config.ProxyURL,
 | 
			
		||||
		apiURL:        "https://api.openai.com/v1/images/generations",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type imgReq struct {
 | 
			
		||||
	Model  string `json:"model"`
 | 
			
		||||
	Prompt string `json:"prompt"`
 | 
			
		||||
	N      int    `json:"n"`
 | 
			
		||||
	Size   string `json:"size"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type imgRes struct {
 | 
			
		||||
	Created int64 `json:"created"`
 | 
			
		||||
	Data    []struct {
 | 
			
		||||
		RevisedPrompt string `json:"revised_prompt"`
 | 
			
		||||
		Url           string `json:"url"`
 | 
			
		||||
	} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ErrRes struct {
 | 
			
		||||
	Error struct {
 | 
			
		||||
		Code    interface{} `json:"code"`
 | 
			
		||||
		Message string      `json:"message"`
 | 
			
		||||
		Param   interface{} `json:"param"`
 | 
			
		||||
		Type    string      `json:"type"`
 | 
			
		||||
	} `json:"error"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f FuncImage) Invoke(params map[string]interface{}) (string, error) {
 | 
			
		||||
	logger.Infof("绘画参数:%+v", params)
 | 
			
		||||
	prompt := utils.InterfaceToString(params["prompt"])
 | 
			
		||||
	// 获取绘图 API KEY
 | 
			
		||||
	var apiKey model.ApiKey
 | 
			
		||||
	f.db.Where("platform = ? AND type = ?", types.OpenAI, "img").Order("last_used_at ASC").First(&apiKey)
 | 
			
		||||
	var res imgRes
 | 
			
		||||
	var errRes ErrRes
 | 
			
		||||
	r, err := req.C().SetProxyURL(f.proxyURL).R().SetHeader("Content-Type", "application/json").
 | 
			
		||||
		SetHeader("Authorization", "Bearer "+apiKey.Value).
 | 
			
		||||
		SetBody(imgReq{
 | 
			
		||||
			Model:  "dall-e-3",
 | 
			
		||||
			Prompt: prompt,
 | 
			
		||||
			N:      1,
 | 
			
		||||
			Size:   "1024x1024",
 | 
			
		||||
		}).
 | 
			
		||||
		SetErrorResult(&errRes).
 | 
			
		||||
		SetSuccessResult(&res).Post(f.apiURL)
 | 
			
		||||
	if err != nil || r.IsErrorState() {
 | 
			
		||||
		return "", fmt.Errorf("error with http request: %v%v%s", err, r.Err, errRes.Error.Message)
 | 
			
		||||
	}
 | 
			
		||||
	// 存储图片
 | 
			
		||||
	imgURL, err := f.uploadManager.GetUploadHandler().PutImg(res.Data[0].Url, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("下载图片失败: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger.Info(imgURL)
 | 
			
		||||
	return fmt.Sprintf("\n\n\n", imgURL), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f FuncImage) Name() string {
 | 
			
		||||
	return f.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ Function = &FuncImage{}
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
package fun
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	"chatplus/service/mj"
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AI 绘画函数
 | 
			
		||||
 | 
			
		||||
type FuncMidJourney struct {
 | 
			
		||||
	name    string
 | 
			
		||||
	service *mj.Service
 | 
			
		||||
	config  types.MidJourneyConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMidJourneyFunc(mjService *mj.Service, config types.MidJourneyConfig) FuncMidJourney {
 | 
			
		||||
	return FuncMidJourney{
 | 
			
		||||
		name:    "MidJourney AI 绘画",
 | 
			
		||||
		config:  config,
 | 
			
		||||
		service: mjService}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f FuncMidJourney) Invoke(params map[string]interface{}) (string, error) {
 | 
			
		||||
	if !f.config.Enabled {
 | 
			
		||||
		return "", errors.New("MidJourney AI 绘画功能没有启用")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger.Infof("MJ 绘画参数:%+v", params)
 | 
			
		||||
	prompt := utils.InterfaceToString(params["prompt"])
 | 
			
		||||
	f.service.PushTask(types.MjTask{
 | 
			
		||||
		SessionId: utils.InterfaceToString(params["session_id"]),
 | 
			
		||||
		Src:       types.TaskSrcChat,
 | 
			
		||||
		Type:      types.TaskImage,
 | 
			
		||||
		Prompt:    prompt,
 | 
			
		||||
		UserId:    utils.IntValue(utils.InterfaceToString(params["user_id"]), 0),
 | 
			
		||||
		RoleId:    utils.IntValue(utils.InterfaceToString(params["role_id"]), 0),
 | 
			
		||||
		Icon:      utils.InterfaceToString(params["icon"]),
 | 
			
		||||
		ChatId:    utils.InterfaceToString(params["chat_id"]),
 | 
			
		||||
	})
 | 
			
		||||
	return prompt, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f FuncMidJourney) Name() string {
 | 
			
		||||
	return f.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ Function = &FuncMidJourney{}
 | 
			
		||||
@@ -3,7 +3,8 @@ package fun
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/core/types"
 | 
			
		||||
	logger2 "chatplus/logger"
 | 
			
		||||
	"chatplus/service/mj"
 | 
			
		||||
	"chatplus/service/oss"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Function interface {
 | 
			
		||||
@@ -29,11 +30,11 @@ type dataItem struct {
 | 
			
		||||
	Remark string `json:"remark"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFunctions(config *types.AppConfig, mjService *mj.Service) map[string]Function {
 | 
			
		||||
func NewFunctions(config *types.AppConfig, db *gorm.DB, manager *oss.UploaderManager) map[string]Function {
 | 
			
		||||
	return map[string]Function{
 | 
			
		||||
		types.FuncZaoBao:     NewZaoBao(config.ApiConfig),
 | 
			
		||||
		types.FuncWeibo:      NewWeiboHot(config.ApiConfig),
 | 
			
		||||
		types.FuncHeadLine:   NewHeadLines(config.ApiConfig),
 | 
			
		||||
		types.FuncMidJourney: NewMidJourneyFunc(mjService, config.MjConfig),
 | 
			
		||||
		types.FuncZaoBao:   NewZaoBao(config.ApiConfig),
 | 
			
		||||
		types.FuncWeibo:    NewWeiboHot(config.ApiConfig),
 | 
			
		||||
		types.FuncHeadLine: NewHeadLines(config.ApiConfig),
 | 
			
		||||
		types.FuncImage:    NewImageFunc(db, manager, config),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,23 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"chatplus/utils"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"time"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	r := time.Now()
 | 
			
		||||
	f := reflect.ValueOf(r)
 | 
			
		||||
	fmt.Println(f.Type().Kind())
 | 
			
		||||
	imgURL := "https://oaidalleapiprodscus.blob.core.windows.net/private/org-UJimNEKhVm07E58nxnjx5FeG/user-e5UAcPVbkm2nwD8urggRRM8q/img-zFXWyrJ9Z1HppI36dZMXNEaA.png?st=2023-11-26T09%3A57%3A49Z&se=2023-11-26T11%3A57%3A49Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2023-11-26T08%3A14%3A59Z&ske=2023-11-27T08%3A14%3A59Z&sks=b&skv=2021-08-06&sig=VmlU9didavbl02XYim2XuMmLMFJsLtCY/ULnzCjeO1g%3D"
 | 
			
		||||
	imageData, err := utils.DownloadImage(imgURL, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	newImagePath := "newimage.png"
 | 
			
		||||
	err = os.WriteFile(newImagePath, imageData, 0644)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("Error writing image file:", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Println("图片保存成功!")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,7 @@
 | 
			
		||||
  <div class="home">
 | 
			
		||||
    <div class="navigator">
 | 
			
		||||
      <div class="logo">
 | 
			
		||||
        <el-link href="/">
 | 
			
		||||
          <el-image :src="logo"/>
 | 
			
		||||
        </el-link>
 | 
			
		||||
 | 
			
		||||
        <el-image :src="logo"/>
 | 
			
		||||
        <div class="divider"></div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -37,8 +34,6 @@
 | 
			
		||||
<script setup>
 | 
			
		||||
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
import {isMobile} from "@/utils/libs";
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,85 +1,88 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="page-invitation">
 | 
			
		||||
    <div class="inner">
 | 
			
		||||
      <h2>会员推广计划</h2>
 | 
			
		||||
      <div class="share-box">
 | 
			
		||||
        <div class="info">
 | 
			
		||||
          我们非常欢迎您把此应用分享给您身边的朋友,分享成功注册后您将获得 <strong>{{ inviteChatCalls }}</strong> 次对话额度以及
 | 
			
		||||
          <strong>{{ inviteImgCalls }}</strong> 次AI绘画额度作为奖励。
 | 
			
		||||
          你可以保存下面的二维码或者直接复制分享您的专属推广链接发送给微信好友。
 | 
			
		||||
  <div class="custom-scroll">
 | 
			
		||||
    <div class="page-invitation" :style="{height: listBoxHeight + 'px'}">
 | 
			
		||||
      <div class="inner">
 | 
			
		||||
        <h2>会员推广计划</h2>
 | 
			
		||||
        <div class="share-box">
 | 
			
		||||
          <div class="info">
 | 
			
		||||
            我们非常欢迎您把此应用分享给您身边的朋友,分享成功注册后您将获得 <strong>{{ inviteChatCalls }}</strong>
 | 
			
		||||
            次对话额度以及
 | 
			
		||||
            <strong>{{ inviteImgCalls }}</strong> 次AI绘画额度作为奖励。
 | 
			
		||||
            你可以保存下面的二维码或者直接复制分享您的专属推广链接发送给微信好友。
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="invite-qrcode">
 | 
			
		||||
            <el-image :src="qrImg"/>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="invite-url">
 | 
			
		||||
            <span>{{ inviteURL }}</span>
 | 
			
		||||
            <el-button type="primary" plain class="copy-link" :data-clipboard-text="inviteURL">复制链接</el-button>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="invite-qrcode">
 | 
			
		||||
          <el-image :src="qrImg"/>
 | 
			
		||||
        <div class="invite-stats">
 | 
			
		||||
          <el-row :gutter="20">
 | 
			
		||||
            <el-col :span="8">
 | 
			
		||||
              <div class="item-box yellow">
 | 
			
		||||
                <el-row :gutter="10">
 | 
			
		||||
                  <el-col :span="10">
 | 
			
		||||
                    <div class="item-icon">
 | 
			
		||||
                      <i class="iconfont icon-role"></i>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                  <el-col :span="14">
 | 
			
		||||
                    <div class="item-info">
 | 
			
		||||
                      <div class="num">{{ hits }}</div>
 | 
			
		||||
                      <div class="text">点击量</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                </el-row>
 | 
			
		||||
              </div>
 | 
			
		||||
            </el-col>
 | 
			
		||||
            <el-col :span="8">
 | 
			
		||||
              <div class="item-box blue">
 | 
			
		||||
                <el-row :gutter="10">
 | 
			
		||||
                  <el-col :span="10">
 | 
			
		||||
                    <div class="item-icon">
 | 
			
		||||
                      <i class="iconfont icon-order"></i>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                  <el-col :span="14">
 | 
			
		||||
                    <div class="item-info">
 | 
			
		||||
                      <div class="num">{{ regNum }}</div>
 | 
			
		||||
                      <div class="text">注册量</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                </el-row>
 | 
			
		||||
              </div>
 | 
			
		||||
            </el-col>
 | 
			
		||||
            <el-col :span="8">
 | 
			
		||||
              <div class="item-box green">
 | 
			
		||||
                <el-row :gutter="10">
 | 
			
		||||
                  <el-col :span="10">
 | 
			
		||||
                    <div class="item-icon">
 | 
			
		||||
                      <i class="iconfont icon-chart"></i>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                  <el-col :span="14">
 | 
			
		||||
                    <div class="item-info">
 | 
			
		||||
                      <div class="num">{{ rate }}%</div>
 | 
			
		||||
                      <div class="text">转化率</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                </el-row>
 | 
			
		||||
              </div>
 | 
			
		||||
            </el-col>
 | 
			
		||||
          </el-row>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="invite-url">
 | 
			
		||||
          <span>{{ inviteURL }}</span>
 | 
			
		||||
          <el-button type="primary" plain class="copy-link" :data-clipboard-text="inviteURL">复制链接</el-button>
 | 
			
		||||
        <h2>您推荐用户</h2>
 | 
			
		||||
 | 
			
		||||
        <div class="invite-logs">
 | 
			
		||||
          <invite-list v-if="isLogin"/>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="invite-stats">
 | 
			
		||||
        <el-row :gutter="20">
 | 
			
		||||
          <el-col :span="8">
 | 
			
		||||
            <div class="item-box yellow">
 | 
			
		||||
              <el-row :gutter="10">
 | 
			
		||||
                <el-col :span="10">
 | 
			
		||||
                  <div class="item-icon">
 | 
			
		||||
                    <i class="iconfont icon-role"></i>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
                <el-col :span="14">
 | 
			
		||||
                  <div class="item-info">
 | 
			
		||||
                    <div class="num">{{ hits }}</div>
 | 
			
		||||
                    <div class="text">点击量</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
              </el-row>
 | 
			
		||||
            </div>
 | 
			
		||||
          </el-col>
 | 
			
		||||
          <el-col :span="8">
 | 
			
		||||
            <div class="item-box blue">
 | 
			
		||||
              <el-row :gutter="10">
 | 
			
		||||
                <el-col :span="10">
 | 
			
		||||
                  <div class="item-icon">
 | 
			
		||||
                    <i class="iconfont icon-order"></i>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
                <el-col :span="14">
 | 
			
		||||
                  <div class="item-info">
 | 
			
		||||
                    <div class="num">{{ regNum }}</div>
 | 
			
		||||
                    <div class="text">注册量</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
              </el-row>
 | 
			
		||||
            </div>
 | 
			
		||||
          </el-col>
 | 
			
		||||
          <el-col :span="8">
 | 
			
		||||
            <div class="item-box green">
 | 
			
		||||
              <el-row :gutter="10">
 | 
			
		||||
                <el-col :span="10">
 | 
			
		||||
                  <div class="item-icon">
 | 
			
		||||
                    <i class="iconfont icon-chart"></i>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
                <el-col :span="14">
 | 
			
		||||
                  <div class="item-info">
 | 
			
		||||
                    <div class="num">{{ rate }}%</div>
 | 
			
		||||
                    <div class="text">转化率</div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </el-col>
 | 
			
		||||
              </el-row>
 | 
			
		||||
            </div>
 | 
			
		||||
          </el-col>
 | 
			
		||||
        </el-row>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <h2>您推荐用户</h2>
 | 
			
		||||
 | 
			
		||||
      <div class="invite-logs">
 | 
			
		||||
        <invite-list v-if="isLogin"/>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -94,11 +97,11 @@ import InviteList from "@/components/InviteList.vue";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
 | 
			
		||||
const listBoxHeight = window.innerHeight
 | 
			
		||||
const inviteURL = ref("")
 | 
			
		||||
const qrImg = ref("")
 | 
			
		||||
const inviteChatCalls = ref(0)
 | 
			
		||||
const inviteImgCalls = ref(0)
 | 
			
		||||
const users = ref([])
 | 
			
		||||
const hits = ref(0)
 | 
			
		||||
const regNum = ref(0)
 | 
			
		||||
const rate = ref(0)
 | 
			
		||||
@@ -150,11 +153,14 @@ onMounted(() => {
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
@import "@/assets/css/custom-scroll.styl"
 | 
			
		||||
.page-invitation {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  background-color: #282c34;
 | 
			
		||||
  height 100vh
 | 
			
		||||
  overflow-x hidden
 | 
			
		||||
  overflow-y visible
 | 
			
		||||
 | 
			
		||||
  .inner {
 | 
			
		||||
    max-width 1000px
 | 
			
		||||
 
 | 
			
		||||
@@ -152,7 +152,6 @@ import RewardVerify from "@/components/RewardVerify.vue";
 | 
			
		||||
import {useRouter} from "vue-router";
 | 
			
		||||
import {removeUserToken} from "@/store/session";
 | 
			
		||||
import UserOrder from "@/components/UserOrder.vue";
 | 
			
		||||
import CountDown from "@/components/CountDown.vue";
 | 
			
		||||
 | 
			
		||||
const listBoxHeight = window.innerHeight - 97
 | 
			
		||||
const list = ref([])
 | 
			
		||||
 
 | 
			
		||||
@@ -124,9 +124,17 @@ import FooterBar from "@/components/FooterBar.vue";
 | 
			
		||||
import SendMsg from "@/components/SendMsg.vue";
 | 
			
		||||
import {validateMobile} from "@/utils/validate";
 | 
			
		||||
import {isMobile} from "@/utils/libs";
 | 
			
		||||
import SendMsgMobile from "@/components/SendMsg.vue";
 | 
			
		||||
import {setUserToken} from "@/store/session";
 | 
			
		||||
import {checkSession} from "@/action/session";
 | 
			
		||||
 | 
			
		||||
checkSession().then(() => {
 | 
			
		||||
  if (isMobile()) {
 | 
			
		||||
    router.push('/mobile')
 | 
			
		||||
  } else {
 | 
			
		||||
    router.push('/chat')
 | 
			
		||||
  }
 | 
			
		||||
}).catch(() => {
 | 
			
		||||
})
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const title = ref('ChatGPT-PLUS 用户注册');
 | 
			
		||||
const formData = ref({
 | 
			
		||||
 
 | 
			
		||||
@@ -66,19 +66,6 @@
 | 
			
		||||
            </el-icon>
 | 
			
		||||
          </el-tooltip>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="启用AI绘画" prop="enabled_draw">
 | 
			
		||||
          <el-switch v-model="system['enabled_draw']"/>
 | 
			
		||||
          <el-tooltip
 | 
			
		||||
              effect="dark"
 | 
			
		||||
              content="需要开启函数功能此配置才会生效"
 | 
			
		||||
              raw-content
 | 
			
		||||
              placement="right"
 | 
			
		||||
          >
 | 
			
		||||
            <el-icon>
 | 
			
		||||
              <InfoFilled/>
 | 
			
		||||
            </el-icon>
 | 
			
		||||
          </el-tooltip>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
 | 
			
		||||
        <el-form-item label="启用众筹功能" prop="enabled_reward">
 | 
			
		||||
          <el-switch v-model="system['enabled_reward']"/>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user