mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	optimize foot copyright snaps
This commit is contained in:
		@@ -7,7 +7,8 @@
 | 
			
		||||
* 功能优化:在应用列表页面,无需先添加模型到用户工作区,可以直接使用
 | 
			
		||||
* 功能新增:MJ 绘图失败的任务不会自动删除,而是会在列表页显示失败详细错误信息
 | 
			
		||||
* 功能新增:允许在管理后台设置首页显示的导航菜单
 | 
			
		||||
* 功能新增:增加 Suno 文生音乐页面功能
 | 
			
		||||
* 功能新增:增加 Suno 文生歌曲功能
 | 
			
		||||
* 功能优化:移除多平台模型支持,统一使用 one-api 接口形式,其他平台的模型需要通过 one-api 接口添加
 | 
			
		||||
 | 
			
		||||
## v4.1.0
 | 
			
		||||
* bug修复:修复移动端修改聊天标题不生效的问题
 | 
			
		||||
 
 | 
			
		||||
@@ -78,3 +78,16 @@ type DallTask struct {
 | 
			
		||||
 | 
			
		||||
	Power int `json:"power"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SunoTask struct {
 | 
			
		||||
	Id           int    `json:"id"`
 | 
			
		||||
	UserId       string `json:"user_id"`
 | 
			
		||||
	Type         int    `json:"type"`
 | 
			
		||||
	TaskId       string `json:"task_id"`
 | 
			
		||||
	Title        string `json:"title"`
 | 
			
		||||
	ReferenceId  string `json:"reference_id"`
 | 
			
		||||
	Prompt       string `json:"prompt"`
 | 
			
		||||
	Tags         string `json:"tags"`
 | 
			
		||||
	Instrumental bool   `json:"instrumental"` // 是否纯音乐
 | 
			
		||||
	ExtendSecs   int    `json:"extend_secs"`  // 延长秒杀
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -49,28 +49,33 @@ func (h *ChatModelHandler) Save(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	item := model.ChatModel{
 | 
			
		||||
		Platform:    data.Platform,
 | 
			
		||||
		Name:        data.Name,
 | 
			
		||||
		Value:       data.Value,
 | 
			
		||||
		Enabled:     data.Enabled,
 | 
			
		||||
		Open:        data.Open,
 | 
			
		||||
		MaxTokens:   data.MaxTokens,
 | 
			
		||||
		MaxContext:  data.MaxContext,
 | 
			
		||||
		Temperature: data.Temperature,
 | 
			
		||||
		KeyId:       data.KeyId,
 | 
			
		||||
		Power:       data.Power}
 | 
			
		||||
	item := model.ChatModel{}
 | 
			
		||||
	// 更新
 | 
			
		||||
	if data.Id > 0 {
 | 
			
		||||
		h.DB.Where("id", data.Id).First(&item)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	item.Name = data.Name
 | 
			
		||||
	item.Value = data.Value
 | 
			
		||||
	item.Enabled = data.Enabled
 | 
			
		||||
	item.SortNum = data.SortNum
 | 
			
		||||
	item.Open = data.Open
 | 
			
		||||
	item.Platform = data.Platform
 | 
			
		||||
	item.Power = data.Power
 | 
			
		||||
	item.MaxTokens = data.MaxTokens
 | 
			
		||||
	item.MaxContext = data.MaxContext
 | 
			
		||||
	item.Temperature = data.Temperature
 | 
			
		||||
	item.KeyId = data.KeyId
 | 
			
		||||
 | 
			
		||||
	var res *gorm.DB
 | 
			
		||||
	if data.Id > 0 {
 | 
			
		||||
		item.Id = data.Id
 | 
			
		||||
		item.SortNum = data.SortNum
 | 
			
		||||
		res = h.DB.Select("*").Omit("created_at").Updates(&item)
 | 
			
		||||
		res = h.DB.Updates(&item)
 | 
			
		||||
	} else {
 | 
			
		||||
		res = h.DB.Create(&item)
 | 
			
		||||
	}
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		logger.Error("error with update database:", res.Error)
 | 
			
		||||
		resp.ERROR(c, "更新数据库失败!")
 | 
			
		||||
		resp.ERROR(c, res.Error.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -112,7 +112,7 @@ func (h *UserHandler) Save(c *gin.Context) {
 | 
			
		||||
		res = h.DB.Select("username", "status", "vip", "power", "chat_roles_json", "chat_models_json", "expired_time").Updates(&user)
 | 
			
		||||
		if res.Error != nil {
 | 
			
		||||
			logger.Error("error with update database:", res.Error)
 | 
			
		||||
			resp.ERROR(c, "更新数据库失败!")
 | 
			
		||||
			resp.ERROR(c, res.Error.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		// 记录算力日志
 | 
			
		||||
@@ -136,6 +136,13 @@ func (h *UserHandler) Save(c *gin.Context) {
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		// 检查用户是否已经存在
 | 
			
		||||
		h.DB.Where("username", data.Username).First(&user)
 | 
			
		||||
		if user.Id > 0 {
 | 
			
		||||
			resp.ERROR(c, "用户名已存在")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		salt := utils.RandString(8)
 | 
			
		||||
		u := model.User{
 | 
			
		||||
			Username:    data.Username,
 | 
			
		||||
 
 | 
			
		||||
@@ -31,9 +31,14 @@ func (h *ChatModelHandler) List(c *gin.Context) {
 | 
			
		||||
	var items []model.ChatModel
 | 
			
		||||
	var chatModels = make([]vo.ChatModel, 0)
 | 
			
		||||
	var res *gorm.DB
 | 
			
		||||
	session := h.DB.Session(&gorm.Session{}).Where("enabled", true)
 | 
			
		||||
	t := c.Query("type")
 | 
			
		||||
	if t != "" {
 | 
			
		||||
		session = session.Where("type", t)
 | 
			
		||||
	}
 | 
			
		||||
	// 如果用户没有登录,则加载所有开放模型
 | 
			
		||||
	if !h.IsLogin(c) {
 | 
			
		||||
		res = h.DB.Where("enabled", true).Where("open", true).Order("sort_num ASC").Find(&items)
 | 
			
		||||
		res = session.Where("open", true).Order("sort_num ASC").Find(&items)
 | 
			
		||||
	} else {
 | 
			
		||||
		user, _ := h.GetLoginUser(c)
 | 
			
		||||
		var models []int
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								api/handler/suno_handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								api/handler/suno_handler.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
package handler
 | 
			
		||||
 | 
			
		||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
			
		||||
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
 | 
			
		||||
// * Use of this source code is governed by a Apache-2.0 license
 | 
			
		||||
// * that can be found in the LICENSE file.
 | 
			
		||||
// * @Author yangjian102621@163.com
 | 
			
		||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"geekai/core"
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SunoHandler struct {
 | 
			
		||||
	BaseHandler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSunoHandler(app *core.AppServer, db *gorm.DB) *SunoHandler {
 | 
			
		||||
	return &SunoHandler{
 | 
			
		||||
		BaseHandler: BaseHandler{
 | 
			
		||||
			App: app,
 | 
			
		||||
			DB:  db,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client WebSocket 客户端,用于通知任务状态变更
 | 
			
		||||
func (h *SunoHandler) Client(c *gin.Context) {
 | 
			
		||||
	//ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
 | 
			
		||||
	//if err != nil {
 | 
			
		||||
	//	logger.Error(err)
 | 
			
		||||
	//	c.Abort()
 | 
			
		||||
	//	return
 | 
			
		||||
	//}
 | 
			
		||||
	//
 | 
			
		||||
	//userId := h.GetInt(c, "user_id", 0)
 | 
			
		||||
	//if userId == 0 {
 | 
			
		||||
	//	logger.Info("Invalid user ID")
 | 
			
		||||
	//	c.Abort()
 | 
			
		||||
	//	return
 | 
			
		||||
	//}
 | 
			
		||||
	//
 | 
			
		||||
	////client := types.NewWsClient(ws)
 | 
			
		||||
	//logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *SunoHandler) Create(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *SunoHandler) List(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *SunoHandler) Remove(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *SunoHandler) Publish(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -29,6 +29,7 @@ type Service struct {
 | 
			
		||||
	notifyQueue *store.RedisQueue
 | 
			
		||||
	db          *gorm.DB
 | 
			
		||||
	running     bool
 | 
			
		||||
	retryCount  map[uint]int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewService(name string, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, db *gorm.DB, cli Client) *Service {
 | 
			
		||||
@@ -39,6 +40,7 @@ func NewService(name string, taskQueue *store.RedisQueue, notifyQueue *store.Red
 | 
			
		||||
		notifyQueue: notifyQueue,
 | 
			
		||||
		Client:      cli,
 | 
			
		||||
		running:     true,
 | 
			
		||||
		retryCount:  make(map[uint]int),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -57,8 +59,13 @@ func (s *Service) Run() {
 | 
			
		||||
		//  如果配置了多个中转平台的 API KEY
 | 
			
		||||
		// U,V 操作必须和 Image 操作属于同一个平台,否则找不到关联任务,需重新放回任务列表
 | 
			
		||||
		if task.ChannelId != "" && task.ChannelId != s.Name {
 | 
			
		||||
			if s.retryCount[task.Id] > 5 {
 | 
			
		||||
				s.db.Model(model.MidJourneyJob{Id: task.Id}).Delete(&model.MidJourneyJob{})
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			logger.Debugf("handle other service task, name: %s, channel_id: %s, drop it.", s.Name, task.ChannelId)
 | 
			
		||||
			s.taskQueue.RPush(task)
 | 
			
		||||
			s.retryCount[task.Id]++
 | 
			
		||||
			time.Sleep(time.Second)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										66
									
								
								api/service/suno/service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								api/service/suno/service.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
package dalle
 | 
			
		||||
 | 
			
		||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
			
		||||
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
 | 
			
		||||
// * Use of this source code is governed by a Apache-2.0 license
 | 
			
		||||
// * that can be found in the LICENSE file.
 | 
			
		||||
// * @Author yangjian102621@163.com
 | 
			
		||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"geekai/core/types"
 | 
			
		||||
	logger2 "geekai/logger"
 | 
			
		||||
	"geekai/service/oss"
 | 
			
		||||
	"geekai/store"
 | 
			
		||||
	"github.com/go-redis/redis/v8"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/imroc/req/v3"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var logger = logger2.GetLogger()
 | 
			
		||||
 | 
			
		||||
type Service struct {
 | 
			
		||||
	httpClient    *req.Client
 | 
			
		||||
	db            *gorm.DB
 | 
			
		||||
	uploadManager *oss.UploaderManager
 | 
			
		||||
	taskQueue     *store.RedisQueue
 | 
			
		||||
	notifyQueue   *store.RedisQueue
 | 
			
		||||
	Clients       *types.LMap[uint, *types.WsClient] // UserId => Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, redisCli *redis.Client) *Service {
 | 
			
		||||
	return &Service{
 | 
			
		||||
		httpClient:    req.C().SetTimeout(time.Minute * 3),
 | 
			
		||||
		db:            db,
 | 
			
		||||
		taskQueue:     store.NewRedisQueue("Suno_Task_Queue", redisCli),
 | 
			
		||||
		notifyQueue:   store.NewRedisQueue("Suno_Notify_Queue", redisCli),
 | 
			
		||||
		Clients:       types.NewLMap[uint, *types.WsClient](),
 | 
			
		||||
		uploadManager: manager,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Service) PushTask(task types.SunoTask) {
 | 
			
		||||
	logger.Infof("add a new Suno task to the task list: %+v", task)
 | 
			
		||||
	s.taskQueue.RPush(task)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Service) Run() {
 | 
			
		||||
	logger.Info("Starting Suno job consumer...")
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			var task types.SunoTask
 | 
			
		||||
			err := s.taskQueue.LPop(&task)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logger.Errorf("taking task with error: %v", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Service) Create(task types.SunoTask) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -12,6 +12,6 @@ type ChatModel struct {
 | 
			
		||||
	MaxTokens   int     `json:"max_tokens"`  // 最大响应长度
 | 
			
		||||
	MaxContext  int     `json:"max_context"` // 最大上下文长度
 | 
			
		||||
	Temperature float32 `json:"temperature"` // 模型温度
 | 
			
		||||
	KeyId       int     `json:"key_id"`
 | 
			
		||||
	KeyId       int     `json:"key_id,omitempty"`
 | 
			
		||||
	KeyName     string  `json:"key_name"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,12 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="foot-container">
 | 
			
		||||
    <div class="footer">
 | 
			
		||||
      <div v-if="license.de_copy" :style="{color:textColor}">{{copyRight}}</div>
 | 
			
		||||
      <div v-else>
 | 
			
		||||
        <span :style="{color:textColor}">{{copyRight}}</span>
 | 
			
		||||
        <div>
 | 
			
		||||
          <a :href="gitURL" target="_blank" :style="{color:textColor}">
 | 
			
		||||
            {{ title }} -
 | 
			
		||||
            {{ version }}
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
      <div><span :style="{color:textColor}">{{copyRight}}</span></div>
 | 
			
		||||
      <div v-if="!license.de_copy">
 | 
			
		||||
        <a :href="gitURL" target="_blank" :style="{color:textColor}">
 | 
			
		||||
          {{ title }} -
 | 
			
		||||
          {{ version }}
 | 
			
		||||
        </a>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -35,7 +32,7 @@ const props = defineProps({
 | 
			
		||||
// 获取系统配置
 | 
			
		||||
httpGet("/api/config/get?key=system").then(res => {
 | 
			
		||||
  title.value = res.data.title??process.env.VUE_APP_TITLE
 | 
			
		||||
  copyRight.value = res.data.copyright??'极客学长 © 2023 - '+new Date().getFullYear()+' All rights reserved.'
 | 
			
		||||
  copyRight.value = res.data.copyright.length>1?res.data.copyright:'极客学长 © 2023 - '+new Date().getFullYear()+' All rights reserved.'
 | 
			
		||||
}).catch(e => {
 | 
			
		||||
  showMessageError("获取系统配置失败:" + e.message)
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="black-input-wrapper">
 | 
			
		||||
    <el-input v-model="model" :type="type" :rows="rows"
 | 
			
		||||
              @input="$emit('update:value', $event)"
 | 
			
		||||
              @input="onInput"
 | 
			
		||||
              style="--el-input-bg-color:#252020;
 | 
			
		||||
            --el-input-border-color:#414141;
 | 
			
		||||
            --el-input-focus-border-color:#414141;
 | 
			
		||||
@@ -9,45 +9,68 @@
 | 
			
		||||
            --el-input-border-radius: 10px;
 | 
			
		||||
            --el-border-color-hover:#616161"
 | 
			
		||||
              resize="none"
 | 
			
		||||
              :placeholder="placeholder"/>
 | 
			
		||||
              :placeholder="placeholder" :maxlength="maxlength"/>
 | 
			
		||||
    <div class="word-stat" v-if="rows > 1">
 | 
			
		||||
      <span>{{value.length}}</span>/<span>{{maxlength}}</span>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'BlackInput',
 | 
			
		||||
  props: {
 | 
			
		||||
    value : {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: '',
 | 
			
		||||
    },
 | 
			
		||||
    placeholder: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: '',
 | 
			
		||||
    },
 | 
			
		||||
    type: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: 'input',
 | 
			
		||||
    },
 | 
			
		||||
    rows: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      default: 5,
 | 
			
		||||
    }
 | 
			
		||||
<script setup>
 | 
			
		||||
 | 
			
		||||
import {ref} from "vue";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  value : {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '',
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      model: this.value
 | 
			
		||||
    }
 | 
			
		||||
  placeholder: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '',
 | 
			
		||||
  },
 | 
			
		||||
  type: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: 'input',
 | 
			
		||||
  },
 | 
			
		||||
  rows: {
 | 
			
		||||
    type: Number,
 | 
			
		||||
    default: 5,
 | 
			
		||||
  },
 | 
			
		||||
  maxlength: {
 | 
			
		||||
    type: Number,
 | 
			
		||||
    default: 1024
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
const model = ref(props.value)
 | 
			
		||||
const emits = defineEmits(['update:value']);
 | 
			
		||||
const onInput = (value) => {
 | 
			
		||||
  emits('update:value',value)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
.black-input-wrapper {
 | 
			
		||||
  position relative
 | 
			
		||||
 | 
			
		||||
  .el-textarea__inner {
 | 
			
		||||
    padding: 20px;
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .word-stat {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    bottom 10px
 | 
			
		||||
    right 10px
 | 
			
		||||
    color rgb(209 203 199)
 | 
			
		||||
    font-family: Neue Montreal, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
 | 
			
		||||
    font-size .875rem
 | 
			
		||||
    line-height 1.25rem
 | 
			
		||||
 | 
			
		||||
    span {
 | 
			
		||||
      margin 0 1px
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -44,9 +44,19 @@
 | 
			
		||||
                  </el-icon>
 | 
			
		||||
                </template>
 | 
			
		||||
              </el-popover>
 | 
			
		||||
              <div class="tag-select">
 | 
			
		||||
                <el-tag
 | 
			
		||||
                  v-for="tag in tags"
 | 
			
		||||
                  :key="tag"
 | 
			
		||||
                  :type="tag === data.tags ? 'success' : ''"
 | 
			
		||||
                  :hit="tag === data.tags"
 | 
			
		||||
                  style="margin-right: 10px"
 | 
			
		||||
                  @click="data.tags = tag"
 | 
			
		||||
                >{{ tag }}</el-tag>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="item">
 | 
			
		||||
              <black-input v-model:value="data.tags" type="textarea" :rows="3" placeholder="请输入音乐风格,多个风格之间用英文逗号隔开..."/>
 | 
			
		||||
              <black-input v-model:value="data.tags" type="textarea" :maxlength="120" :rows="3" placeholder="请输入音乐风格,多个风格之间用英文逗号隔开..."/>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
@@ -64,7 +74,7 @@
 | 
			
		||||
              </el-popover>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="item">
 | 
			
		||||
              <black-input v-model:value="data.title" type="textarea" :rows="2" placeholder="请输入歌曲名称..."/>
 | 
			
		||||
              <black-input v-model:value="data.title" type="textarea" :rows="1" placeholder="请输入歌曲名称..."/>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -180,6 +190,7 @@ const models = ref([
 | 
			
		||||
  {label: "v3.0", value: "chirp-v3-0"},
 | 
			
		||||
  {label: "v3.5", value:"chirp-v3-5"}
 | 
			
		||||
])
 | 
			
		||||
const tags = ref([])
 | 
			
		||||
const data = ref({
 | 
			
		||||
  model: "chirp-v3-0",
 | 
			
		||||
  tags: "",
 | 
			
		||||
 
 | 
			
		||||
@@ -104,10 +104,10 @@
 | 
			
		||||
        <el-form-item label="名称:" prop="name">
 | 
			
		||||
          <el-input v-model="item.name" autocomplete="off"/>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="用途:" prop="type">
 | 
			
		||||
          <el-select v-model="item.type" placeholder="请选择用途" @change="changeType">
 | 
			
		||||
            <el-option v-for="item in types" :value="item.value" :label="item.name" :key="item.value">{{
 | 
			
		||||
                item.name
 | 
			
		||||
        <el-form-item label="类型:" prop="type">
 | 
			
		||||
          <el-select v-model="item.type" placeholder="请选择类型">
 | 
			
		||||
            <el-option v-for="item in types" :value="item.value" :label="item.label" :key="item.value">{{
 | 
			
		||||
                item.label
 | 
			
		||||
              }}
 | 
			
		||||
            </el-option>
 | 
			
		||||
          </el-select>
 | 
			
		||||
@@ -159,13 +159,17 @@ const rules = reactive({
 | 
			
		||||
  type: [{required: true, message: '请选择用途', trigger: 'change',}],
 | 
			
		||||
  value: [{required: true, message: '请输入 API KEY 值', trigger: 'change',}]
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const loading = ref(true)
 | 
			
		||||
const formRef = ref(null)
 | 
			
		||||
const title = ref("")
 | 
			
		||||
const platforms = ref([])
 | 
			
		||||
const types = ref([
 | 
			
		||||
  {name: "聊天", value: "chat"},
 | 
			
		||||
  {name: "绘画", value: "img"},
 | 
			
		||||
  {label: "对话", value:"chat"},
 | 
			
		||||
  {label: "Midjourney", value:"mj"},
 | 
			
		||||
  {label: "DALL-E", value:"dall"},
 | 
			
		||||
  {label: "Suno文生歌", value:"suno"},
 | 
			
		||||
  {label: "Luma视频", value:"luma"},
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -46,11 +46,6 @@
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
 | 
			
		||||
        <!--        <el-table-column label="创建时间">-->
 | 
			
		||||
        <!--          <template #default="scope">-->
 | 
			
		||||
        <!--            <span>{{ dateFormat(scope.row['created_at']) }}</span>-->
 | 
			
		||||
        <!--          </template>-->
 | 
			
		||||
        <!--        </el-table-column>-->
 | 
			
		||||
        <el-table-column prop="key_name" label="绑定API-KEY"/>
 | 
			
		||||
        <el-table-column label="操作" width="180">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
@@ -89,7 +84,7 @@
 | 
			
		||||
          <el-input v-model="item.value" autocomplete="off"/>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
 | 
			
		||||
        <el-form-item label="费率:" prop="weight">
 | 
			
		||||
        <el-form-item label="消耗算力:" prop="weight">
 | 
			
		||||
          <template #default>
 | 
			
		||||
            <div class="tip-input">
 | 
			
		||||
              <el-input-number :min="0" v-model="item.power" autocomplete="off"/>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user