Compare commits
2 Commits
79afa037e2
...
7379488839
| Author | SHA1 | Date | |
|---|---|---|---|
| 7379488839 | |||
| 4d76899488 |
@ -5,10 +5,6 @@ VITE_BASE = '/'
|
||||
VITE_API_PREFIX = '/api'
|
||||
VITE_API_BASE_URL = 'http://test.xueai.art/newapi/api' # http://huanda.xueai.art http://106.54.11.219/api 43.248.131.153:8003
|
||||
|
||||
# 支付服务
|
||||
VITE_API_PAY_PREFIX = '/pay'
|
||||
VITE_API_PAY_TARGET = 'http://test.xueai.art' # http://43.248.133.202 test.xueai.art
|
||||
|
||||
# 任务处理模块
|
||||
VITE_API_WORKFLOW_UPLOAD = 'http://test.xueai.art/AIGC/Temp/uploadImage' # https://sxwz.xueai.art/workflow https://designtools.xueai.art/workflow
|
||||
VITE_API_TASK_PREFIX = '/suanli'
|
||||
|
||||
@ -6,21 +6,15 @@ VITE_BUILD_MOCK = false
|
||||
|
||||
# 主服务
|
||||
VITE_API_PREFIX = '/api'
|
||||
VITE_API_BASE_URL = 'https://sxwz.xueai.art/api'
|
||||
|
||||
# 支付服务
|
||||
VITE_API_PAY_PREFIX = '/pay'
|
||||
VITE_API_PAY_TARGET = 'https://sxwz.xueai.art' # http://43.248.133.202
|
||||
VITE_API_BASE_URL = 'https://sxwz.xueai.art/newapi/api'
|
||||
|
||||
# 任务处理模块
|
||||
VITE_API_WORKFLOW_UPLOAD = 'https://designtools.xueai.art/workflow/file/upload' # https://sxwz.xueai.art/workflow https://designtools.xueai.art/workflow
|
||||
VITE_API_WORKFLOW_UPLOAD = 'https://resources.xueai.art/AIGC/Temp/uploadImage' # https://sxwz.xueai.art/workflow https://designtools.xueai.art/workflow
|
||||
VITE_API_TASK_PREFIX = '/suanli'
|
||||
VITE_API_TASK_TARGET = 'http://test.xueai.art'
|
||||
|
||||
# 模型资源
|
||||
VITE_API_MODEL_RESOURCE = 'https://resources.xueai.art/AIGC'
|
||||
VITE_API_TASK_TARGET = 'https://sxwz.xueai.art'
|
||||
|
||||
# 是否开启KKFileView
|
||||
FILE_OPEN_PREVIEW = false
|
||||
# KKFileView服务器地址
|
||||
# FILE_VIEW_SERVER_URL = 'http://192.168.122.209:8012'
|
||||
# FILE_VIEW_SERVER_URL = 'http://192.168.122.209:8012'.
|
||||
|
||||
|
||||
44
CLAUDE.md
44
CLAUDE.md
@ -18,9 +18,9 @@ Vue 3 (Composition API) + Vite 7 + Pinia + Vue Router + Element Plus + `vue-elem
|
||||
|
||||
## 架构概览
|
||||
|
||||
AI 绘画/视频生成前端操作平台,通过 HTTP 接口对接算力调度后端(suanli),提交生成任务并轮询结果。
|
||||
AI 绘画/视频/音乐生成前端操作平台,通过 HTTP 接口对接算力调度后端(suanli),提交生成任务并轮询结果。
|
||||
|
||||
**核心架构:Platform Descriptor 模式。** Painting 和 Video 是两个独立的平台包,通过统一的注册表动态加载。dialogBox 是通用编排壳,不包含任何平台特定逻辑。
|
||||
**核心架构:Platform Descriptor 模式。** Painting、Video 和 Music 是三个独立的平台包,通过统一的注册表动态加载。dialogBox 是通用编排壳,不包含任何平台特定逻辑。
|
||||
|
||||
### 关键目录
|
||||
|
||||
@ -54,6 +54,15 @@ src/
|
||||
│ ├── pattern.vue
|
||||
│ ├── proportion.vue
|
||||
│ └── time.vue
|
||||
│ └── music/ # Music 平台
|
||||
│ ├── index.js # defineMusicPlatform()
|
||||
│ ├── modelSelector.vue
|
||||
│ ├── imageUploader.vue
|
||||
│ └── controls/
|
||||
│ ├── modeSelector.vue # 模式选择(常用/专业/Remix/编辑)
|
||||
│ ├── pureMusicGroup.vue # 纯音乐开关 + 歌词输入弹窗
|
||||
│ ├── lyricsInput.vue # 专业模式歌词输入
|
||||
│ └── timeControl.vue # 时长滑块(常用模式)
|
||||
├── stores/ # Pinia 状态管理
|
||||
│ ├── user.js # 用户认证、信息(含 sessionId),pinia persist 持久化 token
|
||||
│ ├── display.js # 生成历史列表、UI 状态(滚动、画布等)
|
||||
@ -234,6 +243,36 @@ Video 的 `getDefaultModel()` 返回 `''`,不硬编码 UUID;模型列表加
|
||||
}
|
||||
```
|
||||
|
||||
### Music 平台特有行为
|
||||
|
||||
Music 平台与 Painting/Video 的关键差异:
|
||||
|
||||
- **模式驱动控件显隐**:`mode` ref(常用模式/专业模式/Remix模式/编辑模式)决定大部分控件的 `show()`。切换模式时 `visibleControls` 自动更新。
|
||||
- **纯音乐/歌词互斥**:常用模式下 `pureMusicGroup` 控件内含纯音乐开关 + 歌词输入弹窗。开关开启时清空歌词;关闭时弹出歌词输入框。`buildTaskBody` 中 `pureMusic` 为 true 时 `lyrics` 为空字符串。
|
||||
- **参考音频**:`imageUploader` 用于上传音频文件(非图片),仅在专业模式下显示(`showImageUploader()` → `mode === '专业模式'`),且为必填(`isImageRequired()` → true)。
|
||||
- **数量限制**:专业模式下 `quantity` 强制为 1(`props()` 中做 `Math.min(mode === '专业模式' ? 1 : maxQty)`)。
|
||||
- **Music 独有控件**:
|
||||
|
||||
| 控件 | `name` | `beforeModel` | 作用 |
|
||||
|------|--------|---------------|------|
|
||||
| `ModeSelector` | `modeSelector` | `true`(在模型选择器前) | 常用/专业/Remix/编辑 四选一 |
|
||||
| `PureMusicGroup` | `pureMusicGroup` | `false` | 纯音乐开关 + 歌词输入(仅常用模式显示) |
|
||||
| `LyricsInput` | `lyricsInput` | `false` | 歌词输入(仅专业模式显示) |
|
||||
| `TimeControl` | `timeControl` | `false` | 时长滑块 min~max(仅常用模式显示) |
|
||||
|
||||
- **`handledUis`**(Music ParamGroup 过滤用):`['textarea', 'proportion', 'imageUpload', 'hidden', 'quantity']`,额外按名称排除 `['mode', 'pureMusic', 'lyrics', 'duration', 'quantity']`。
|
||||
|
||||
### 计费类型映射(`getChargeType`)
|
||||
|
||||
`taskPolling.js` 中的 `getChargeType(chargeType)` 将平台类型映射为计费数字:
|
||||
|
||||
| 平台 | 计费码 |
|
||||
|------|--------|
|
||||
| Painting | 1 |
|
||||
| Video | 4 |
|
||||
| Music | 5 |
|
||||
| 其他 | 2 |
|
||||
|
||||
### 组件规范
|
||||
|
||||
**禁止在项目组件中使用外部 UI 库**(Element Plus 等),图标除外。自定义组件使用项目自研的 `Select`、`Popover` 或纯 CSS 实现。`SwitchControl` 即为一例——纯 CSS 滑动开关,不依赖 `el-switch`。
|
||||
@ -358,6 +397,7 @@ FILE_OPEN_PREVIEW = true # 是否开启 KKFileView 预览
|
||||
|------|----------|
|
||||
| Painting | `ai_painting_talk` |
|
||||
| Video | `ai_video_talk` |
|
||||
| Music | `ai_music_talk` |
|
||||
|
||||
映射函数 `getPlatformCode()` 位于 `utils/modelApi.js`。
|
||||
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
{
|
||||
"modelName": "海螺 02-fast 图生视频",
|
||||
"modelDescription": "RunningHub MiniMax 海螺 02-fast 图生视频模型,基于参考图生成快节奏电影感动画",
|
||||
"endpoint": "/minimax/hailuo-02/fast",
|
||||
"params": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"ui": "textarea",
|
||||
"label": "提示词",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"name": "enablePromptExpansion",
|
||||
"ui": "switch",
|
||||
"label": "提示词扩展",
|
||||
"default": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "imageUrl",
|
||||
"ui": "imageUpload",
|
||||
"label": "参考图片",
|
||||
"maxCount": 1,
|
||||
"required": true,
|
||||
"maxSizeMB": 10
|
||||
},
|
||||
{
|
||||
"name": "duration",
|
||||
"ui": "select",
|
||||
"label": "时长(秒)",
|
||||
"default": "6",
|
||||
"options": ["6", "10"],
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"promptPlaceholder": "描述基于原图的动画效果(可选)。",
|
||||
"inputType": "image",
|
||||
"maxImages": 1
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
{
|
||||
"modelName": "LTX-2.3 图生视频",
|
||||
"modelDescription": "RunningHub LTX-2.3 图生视频模型,基于图片生成视频",
|
||||
"endpoint": "/rhart-video/ltx-2.3/image-to-video",
|
||||
"params": [
|
||||
{
|
||||
"name": "imageUrl",
|
||||
"ui": "imageUpload",
|
||||
"label": "参考图片",
|
||||
"maxCount": 1,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "prompt",
|
||||
"ui": "textarea",
|
||||
"label": "提示词",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "aspectRatio",
|
||||
"ui": "proportion",
|
||||
"label": "画面比例",
|
||||
"default": "16:9",
|
||||
"options": ["9:16", "16:9"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "resolution",
|
||||
"ui": "resolution",
|
||||
"label": "分辨率",
|
||||
"default": "720p",
|
||||
"options": ["480p", "720p", "1080p"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "duration",
|
||||
"ui": "select",
|
||||
"label": "时长(秒)",
|
||||
"default": 5,
|
||||
"options": [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"promptPlaceholder": "描述图片和期望的视频运动效果。",
|
||||
"inputType": "image",
|
||||
"maxImages": 1
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
{
|
||||
"modelName": "LTX-2.3 文生视频",
|
||||
"modelDescription": "RunningHub LTX-2.3 文生视频模型(Text-to-Video),基于文本生成视频",
|
||||
"endpoint": "/rhart-video/ltx-2.3/text-to-video",
|
||||
"params": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"ui": "textarea",
|
||||
"label": "提示词",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "resolution",
|
||||
"ui": "select",
|
||||
"label": "分辨率",
|
||||
"default": "720p",
|
||||
"options": ["1080p", "720p", "480p"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "aspectRatio",
|
||||
"ui": "proportion",
|
||||
"label": "画面比例",
|
||||
"default": "16:9",
|
||||
"options": ["16:9", "9:16"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "duration",
|
||||
"ui": "number",
|
||||
"label": "时长(秒)",
|
||||
"default": 5,
|
||||
"min": 5,
|
||||
"max": 15,
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"promptPlaceholder": "描述你想生成的画面和动作。",
|
||||
"inputType": "text"
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
{
|
||||
"modelName": "Vidu 首尾帧生视频 q3-turbo",
|
||||
"modelDescription": "RunningHub Vidu 首尾帧生视频 q3-turbo 模型,通过首尾帧图片驱动视频生成,支持音视频直出",
|
||||
"endpoint": "/vidu/start-end-to-video-q3-turbo",
|
||||
"params": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"ui": "textarea",
|
||||
"label": "提示词",
|
||||
"required": true,
|
||||
"maxLength": 4000
|
||||
},
|
||||
{
|
||||
"name": "firstImageUrl",
|
||||
"ui": "imageUpload",
|
||||
"label": "首帧图片",
|
||||
"maxCount": 1,
|
||||
"required": true,
|
||||
"maxSizeMB": 50
|
||||
},
|
||||
{
|
||||
"name": "lastImageUrl",
|
||||
"ui": "imageUpload",
|
||||
"label": "尾帧图片",
|
||||
"maxCount": 1,
|
||||
"required": true,
|
||||
"maxSizeMB": 50
|
||||
},
|
||||
{
|
||||
"name": "duration",
|
||||
"ui": "select",
|
||||
"label": "时长(秒)",
|
||||
"default": "5",
|
||||
"options": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "resolution",
|
||||
"ui": "resolution",
|
||||
"label": "分辨率",
|
||||
"default": "720p",
|
||||
"options": ["540p", "720p", "1080p"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "movementAmplitude",
|
||||
"ui": "select",
|
||||
"label": "运动幅度",
|
||||
"default": "auto",
|
||||
"options": ["auto", "small", "medium", "large"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
"ui": "switch",
|
||||
"label": "音频直出",
|
||||
"default": true,
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"promptPlaceholder": "描述首尾帧之间的动作补全。文本长度限制 1-4000。",
|
||||
"inputType": "image",
|
||||
"maxImages": 2
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
{
|
||||
"modelName": "Vidu 文生视频 q3-turbo",
|
||||
"modelDescription": "RunningHub Vidu 文生视频 q3-turbo 模型,支持音视频直出",
|
||||
"endpoint": "/vidu/text-to-video-q3-turbo",
|
||||
"params": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"ui": "textarea",
|
||||
"label": "提示词",
|
||||
"required": true,
|
||||
"maxLength": 4000
|
||||
},
|
||||
{
|
||||
"name": "style",
|
||||
"ui": "select",
|
||||
"label": "风格",
|
||||
"default": "general",
|
||||
"options": ["general", "anime"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "aspectRatio",
|
||||
"ui": "proportion",
|
||||
"label": "画面比例",
|
||||
"default": "16:9",
|
||||
"options": ["4:3", "3:4", "16:9", "9:16", "1:1"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "resolution",
|
||||
"ui": "resolution",
|
||||
"label": "分辨率",
|
||||
"default": "720p",
|
||||
"options": ["540p", "720p", "1080p"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "duration",
|
||||
"ui": "select",
|
||||
"label": "时长(秒)",
|
||||
"default": "5",
|
||||
"options": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
"ui": "switch",
|
||||
"label": "音频直出",
|
||||
"default": true,
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"promptPlaceholder": "描述你想生成的视频画面和音频。文本长度限制 1-4000。",
|
||||
"inputType": "text"
|
||||
}
|
||||
@ -157,7 +157,8 @@ const handleStart = async () => {
|
||||
modelType: p.modelType.value,
|
||||
prompt: prompt.value,
|
||||
referenceImages: referenceImages.value,
|
||||
modelParams: body
|
||||
modelParams: body,
|
||||
...(p.getGenerateDataExtras?.() || {})
|
||||
}
|
||||
|
||||
const data = {
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import { markRaw, reactive, ref, computed } from 'vue'
|
||||
import { registerPlatform } from '@/platforms/registry.js'
|
||||
import { fetchPlatformModels, getPlatformCode, getModelConfig, getModelId, preloadModelConfigs } from '@/utils/modelApi'
|
||||
import { syncParamValues, checkShowWhen } from '@/utils/modelConfigHelper.js'
|
||||
import ModelSelector from './modelSelector.vue'
|
||||
import ImageUploader from './imageUploader.vue'
|
||||
import ModeSelector from './controls/modeSelector.vue'
|
||||
import PureMusicGroup from './controls/pureMusicGroup.vue'
|
||||
import LyricsInput from './controls/lyricsInput.vue'
|
||||
import TimeControl from './controls/timeControl.vue'
|
||||
import { computed, markRaw, reactive, ref } from 'vue'
|
||||
import ParamGroup from '@/components/ParamGroup/index.vue'
|
||||
import Select from '@/components/Select/index.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { registerPlatform } from '@/platforms/registry.js'
|
||||
import { fetchPlatformModels, getModelConfig, getModelId, getPlatformCode, preloadModelConfigs } from '@/utils/modelApi'
|
||||
import { checkShowWhen } from '@/utils/modelConfigHelper.js'
|
||||
import LyricsInput from './controls/lyricsInput.vue'
|
||||
import ModeSelector from './controls/modeSelector.vue'
|
||||
import PureMusicGroup from './controls/pureMusicGroup.vue'
|
||||
import TimeControl from './controls/timeControl.vue'
|
||||
import ImageUploader from './imageUploader.vue'
|
||||
import ModelSelector from './modelSelector.vue'
|
||||
|
||||
// 由专用控件处理的 ui 类型
|
||||
const handledUis = ['textarea', 'proportion', 'imageUpload', 'hidden', 'quantity']
|
||||
@ -37,11 +36,11 @@ export function defineMusicPlatform() {
|
||||
async function loadModels() {
|
||||
models.value = await fetchPlatformModels(code.value)
|
||||
if (!model.value && models.value.length) {
|
||||
const first = models.value.find(m => !m.disabled)
|
||||
const first = models.value.find((m) => !m.disabled)
|
||||
if (first) model.value = first.id
|
||||
}
|
||||
if (models.value.length) {
|
||||
const ids = models.value.map(m => m.id)
|
||||
const ids = models.value.map((m) => m.id)
|
||||
preloadModelConfigs(ids)
|
||||
}
|
||||
}
|
||||
@ -65,22 +64,22 @@ export function defineMusicPlatform() {
|
||||
})
|
||||
|
||||
// 同步专用 ref
|
||||
const modeParam = config.params.find(p => p.name === 'mode' || p.ui === 'mode')
|
||||
const modeParam = config.params.find((p) => p.name === 'mode' || p.ui === 'mode')
|
||||
if (modeParam) mode.value = modeParam.default || '常用模式'
|
||||
|
||||
const qtyParam = config.params.find(p => p.ui === 'quantity')
|
||||
const qtyParam = config.params.find((p) => p.ui === 'quantity')
|
||||
if (qtyParam) quantity.value = qtyParam.default || 1
|
||||
|
||||
const durParam = config.params.find(p => p.name === 'duration')
|
||||
const durParam = config.params.find((p) => p.name === 'duration')
|
||||
if (durParam) duration.value = durParam.default || 'Auto'
|
||||
|
||||
const lyricsParam = config.params.find(p => p.name === 'lyrics')
|
||||
const lyricsParam = config.params.find((p) => p.name === 'lyrics')
|
||||
if (lyricsParam) lyrics.value = lyricsParam.default || ''
|
||||
|
||||
const seedParam = config.params.find(p => p.name === 'randomSeed' || p.name === 'seed')
|
||||
const seedParam = config.params.find((p) => p.name === 'randomSeed' || p.name === 'seed')
|
||||
if (seedParam) randomSeed.value = seedParam.default || ''
|
||||
|
||||
const pmParam = config.params.find(p => p.name === 'pureMusic')
|
||||
const pmParam = config.params.find((p) => p.name === 'pureMusic')
|
||||
if (pmParam) pureMusic.value = pmParam.default !== undefined ? pmParam.default : true
|
||||
|
||||
if (config.promptPlaceholder) {
|
||||
@ -93,7 +92,7 @@ export function defineMusicPlatform() {
|
||||
function imageUploadLimit() {
|
||||
if (!modelConfig.value) return 0
|
||||
return modelConfig.value.params
|
||||
.filter(p => p.ui === 'imageUpload')
|
||||
.filter((p) => p.ui === 'imageUpload')
|
||||
.reduce((sum, p) => sum + (p.maxCount || 1), 0)
|
||||
}
|
||||
|
||||
@ -118,12 +117,12 @@ export function defineMusicPlatform() {
|
||||
function buildTaskBody({ prompt, referenceImages }) {
|
||||
syncMusicParamValues()
|
||||
// 将 prompt 写入 paramValues(如果 config 中有 prompt 参数)
|
||||
const promptParam = modelConfig.value?.params?.find(p => p.ui === 'textarea')
|
||||
const promptParam = modelConfig.value?.params?.find((p) => p.ui === 'textarea')
|
||||
if (promptParam) paramValues[promptParam.name] = prompt
|
||||
|
||||
// 将参考音频映射到 imageUpload 参数
|
||||
if (modelConfig.value) {
|
||||
const imageUploadParams = modelConfig.value.params.filter(p => p.ui === 'imageUpload')
|
||||
const imageUploadParams = modelConfig.value.params.filter((p) => p.ui === 'imageUpload')
|
||||
imageUploadParams.forEach((p, i) => {
|
||||
if (referenceAudio.value[i]) {
|
||||
paramValues[p.name] = referenceAudio.value[i].url
|
||||
@ -133,26 +132,34 @@ export function defineMusicPlatform() {
|
||||
return { ...paramValues }
|
||||
}
|
||||
|
||||
// 附加平台专属字段到 generateData,用于任务列表展示和再次生成/重新编辑
|
||||
function getGenerateDataExtras() {
|
||||
return {
|
||||
pureMusic: pureMusic.value,
|
||||
mode: mode.value
|
||||
}
|
||||
}
|
||||
|
||||
function syncMusicParamValues() {
|
||||
if (!modelConfig.value) return
|
||||
const config = modelConfig.value
|
||||
|
||||
const qtyParam = config.params.find(p => p.ui === 'quantity')
|
||||
const qtyParam = config.params.find((p) => p.ui === 'quantity')
|
||||
if (qtyParam) paramValues[qtyParam.name] = quantity.value
|
||||
|
||||
const durParam = config.params.find(p => p.name === 'duration')
|
||||
const durParam = config.params.find((p) => p.name === 'duration')
|
||||
if (durParam) paramValues[durParam.name] = duration.value
|
||||
|
||||
const lyricsParam = config.params.find(p => p.name === 'lyrics')
|
||||
const lyricsParam = config.params.find((p) => p.name === 'lyrics')
|
||||
if (lyricsParam) paramValues[lyricsParam.name] = lyrics.value
|
||||
|
||||
const seedParam = config.params.find(p => p.name === 'randomSeed' || p.name === 'seed')
|
||||
const seedParam = config.params.find((p) => p.name === 'randomSeed' || p.name === 'seed')
|
||||
if (seedParam) paramValues[seedParam.name] = randomSeed.value
|
||||
|
||||
const pmParam = config.params.find(p => p.name === 'pureMusic')
|
||||
const pmParam = config.params.find((p) => p.name === 'pureMusic')
|
||||
if (pmParam) paramValues[pmParam.name] = pureMusic.value
|
||||
|
||||
const modeParam = config.params.find(p => p.name === 'mode' || p.ui === 'mode')
|
||||
const modeParam = config.params.find((p) => p.name === 'mode' || p.ui === 'mode')
|
||||
if (modeParam) paramValues[modeParam.name] = mode.value
|
||||
}
|
||||
|
||||
@ -171,13 +178,13 @@ export function defineMusicPlatform() {
|
||||
name: 'modeSelector',
|
||||
component: markRaw(ModeSelector),
|
||||
beforeModel: true,
|
||||
show: (config) => !!config?.params?.find(p => p.name === 'mode' || p.ui === 'mode'),
|
||||
show: (config) => !!config?.params?.find((p) => p.name === 'mode' || p.ui === 'mode'),
|
||||
props: (config) => {
|
||||
const modeParam = config?.params?.find(p => p.name === 'mode' || p.ui === 'mode')
|
||||
const modeParam = config?.params?.find((p) => p.name === 'mode' || p.ui === 'mode')
|
||||
return {
|
||||
modelValue: mode.value,
|
||||
'modelValue': mode.value,
|
||||
'onUpdate:modelValue': (v) => { mode.value = v },
|
||||
options: modeParam?.options || []
|
||||
'options': modeParam?.options || []
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -185,11 +192,11 @@ export function defineMusicPlatform() {
|
||||
name: 'pureMusicGroup',
|
||||
component: markRaw(PureMusicGroup),
|
||||
beforeModel: false,
|
||||
show: (config) => mode.value === '常用模式' && !!config?.params?.find(p => p.name === 'pureMusic'),
|
||||
show: (config) => mode.value === '常用模式' && !!config?.params?.find((p) => p.name === 'pureMusic'),
|
||||
props: (config) => ({
|
||||
modelValue: pureMusic.value,
|
||||
'modelValue': pureMusic.value,
|
||||
'onUpdate:modelValue': (v) => { pureMusic.value = v },
|
||||
lyrics: lyrics.value,
|
||||
'lyrics': lyrics.value,
|
||||
'onUpdate:lyrics': (v) => { lyrics.value = v }
|
||||
})
|
||||
},
|
||||
@ -197,9 +204,9 @@ export function defineMusicPlatform() {
|
||||
name: 'lyricsInput',
|
||||
component: markRaw(LyricsInput),
|
||||
beforeModel: false,
|
||||
show: (config) => mode.value === '专业模式' && !!config?.params?.find(p => p.name === 'lyrics'),
|
||||
show: (config) => mode.value === '专业模式' && !!config?.params?.find((p) => p.name === 'lyrics'),
|
||||
props: (config) => ({
|
||||
modelValue: lyrics.value,
|
||||
'modelValue': lyrics.value,
|
||||
'onUpdate:modelValue': (v) => { lyrics.value = v }
|
||||
})
|
||||
},
|
||||
@ -207,14 +214,14 @@ export function defineMusicPlatform() {
|
||||
name: 'timeControl',
|
||||
component: markRaw(TimeControl),
|
||||
beforeModel: false,
|
||||
show: (config) => mode.value === '常用模式' && !!config?.params?.find(p => p.name === 'duration'),
|
||||
show: (config) => mode.value === '常用模式' && !!config?.params?.find((p) => p.name === 'duration'),
|
||||
props: (config) => {
|
||||
const durParam = config?.params?.find(p => p.name === 'duration')
|
||||
const durParam = config?.params?.find((p) => p.name === 'duration')
|
||||
return {
|
||||
modelValue: duration.value,
|
||||
'modelValue': duration.value,
|
||||
'onUpdate:modelValue': (v) => { duration.value = v },
|
||||
min: durParam?.min || 10,
|
||||
max: durParam?.max || 240
|
||||
'min': durParam?.min || 10,
|
||||
'max': durParam?.max || 240
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -222,15 +229,15 @@ export function defineMusicPlatform() {
|
||||
name: 'quantity',
|
||||
component: markRaw(Select),
|
||||
beforeModel: false,
|
||||
show: (config) => !!config?.params?.find(p => p.ui === 'quantity'),
|
||||
show: (config) => !!config?.params?.find((p) => p.ui === 'quantity'),
|
||||
props: (config) => {
|
||||
const qtyParam = config?.params?.find(p => p.ui === 'quantity')
|
||||
const qtyParam = config?.params?.find((p) => p.ui === 'quantity')
|
||||
const maxQty = Math.max(...(qtyParam?.options || [1]))
|
||||
const limited = mode.value === '专业模式' ? 1 : maxQty
|
||||
return {
|
||||
modelValue: quantity.value,
|
||||
'modelValue': quantity.value,
|
||||
'onUpdate:modelValue': (v) => { quantity.value = v },
|
||||
options: Array.from({ length: limited }, (_, i) => ({ value: i + 1, label: `${i + 1}条` }))
|
||||
'options': Array.from({ length: limited }, (_, i) => ({ value: i + 1, label: `${i + 1}条` }))
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -263,15 +270,36 @@ export function defineMusicPlatform() {
|
||||
controls,
|
||||
ImageUploader: markRaw(ImageUploader),
|
||||
state: {
|
||||
model, modelType, mode, modelConfig, paramValues,
|
||||
promptPlaceholder, referenceAudio, models,
|
||||
quantity, duration, lyrics, randomSeed, pureMusic
|
||||
model,
|
||||
modelType,
|
||||
mode,
|
||||
modelConfig,
|
||||
paramValues,
|
||||
promptPlaceholder,
|
||||
referenceAudio,
|
||||
models,
|
||||
quantity,
|
||||
duration,
|
||||
lyrics,
|
||||
randomSeed,
|
||||
pureMusic
|
||||
},
|
||||
model, modelType, mode, modelConfig, promptPlaceholder,
|
||||
loadModels, loadConfig, getDefaultModel,
|
||||
imageUploadLimit, validateBeforeSubmit,
|
||||
getUploaderBindings, showImageUploader, isImageRequired,
|
||||
buildTaskBody, fillFromResult
|
||||
model,
|
||||
modelType,
|
||||
mode,
|
||||
modelConfig,
|
||||
promptPlaceholder,
|
||||
loadModels,
|
||||
loadConfig,
|
||||
getDefaultModel,
|
||||
imageUploadLimit,
|
||||
validateBeforeSubmit,
|
||||
getUploaderBindings,
|
||||
showImageUploader,
|
||||
isImageRequired,
|
||||
buildTaskBody,
|
||||
fillFromResult,
|
||||
getGenerateDataExtras
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,8 @@ function saveToCache(code, models) {
|
||||
|
||||
// 类型 → 平台编码映射
|
||||
export function getPlatformCode(type) {
|
||||
switch (type) {
|
||||
const normalized = type.charAt(0).toUpperCase() + type.slice(1).toLowerCase()
|
||||
switch (normalized) {
|
||||
case 'Painting':
|
||||
return 'ai_painting_talk'
|
||||
case 'Video':
|
||||
|
||||
@ -2,10 +2,12 @@ import { ElNotification } from 'element-plus'
|
||||
import { h } from 'vue'
|
||||
import { requestCreateTask, requestTaskStatus } from '@/apis/display'
|
||||
import { useDisplayStore, useUserStore } from '@/stores'
|
||||
import { getPlatformCode } from '@/utils/modelApi'
|
||||
import { userError } from '@/utils/tokenError'
|
||||
|
||||
export function getChargeType(chargeType) {
|
||||
switch (chargeType) {
|
||||
const normalized = chargeType.charAt(0).toUpperCase() + chargeType.slice(1).toLowerCase()
|
||||
switch (normalized) {
|
||||
case 'Painting':
|
||||
return 1
|
||||
case 'Video':
|
||||
@ -66,17 +68,18 @@ export async function generate(data, generateData) {
|
||||
let taskId = null
|
||||
let pollInterval = null
|
||||
|
||||
useDisplay.isSubGerenate = true
|
||||
|
||||
if (!data.modelId) {
|
||||
ElNotification({
|
||||
title: '生成失败',
|
||||
message: h('i', { style: 'color: teal' }, '未找到模型ID,请联系管理员配置'),
|
||||
type: 'error'
|
||||
})
|
||||
useDisplay.isSubGerenate = false
|
||||
return
|
||||
}
|
||||
|
||||
useDisplay.isSubGerenate = true
|
||||
|
||||
// 从登录态获取 sessionId
|
||||
const sessionId = useUserStore().userInfo.sessionId
|
||||
if (!sessionId) {
|
||||
@ -96,7 +99,8 @@ export async function generate(data, generateData) {
|
||||
const requestBody = {
|
||||
model_id: data.modelId,
|
||||
body,
|
||||
request: data.request
|
||||
request: data.request,
|
||||
platform_code: getPlatformCode(data.type)
|
||||
}
|
||||
|
||||
// POST 创建任务
|
||||
|
||||
@ -10,13 +10,13 @@
|
||||
</span>
|
||||
<div :style="{ visibility: !isHovering && !showExternalGenerateData ? 'visible' : 'hidden' }" class="generate-data internal">
|
||||
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
||||
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
||||
<div class="detailed-data">{{ secondTagText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="!isHovering && showExternalGenerateData" class="generate-data external">
|
||||
<div class="detailed-data first-detailed-data">{{ props.item.generateData.model }}</div>
|
||||
<div class="detailed-data">{{ props.item.generateData.proportion }}</div>
|
||||
<div class="detailed-data">{{ secondTagText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -212,6 +212,22 @@ const generateStatusText = computed(() => {
|
||||
return ''
|
||||
})
|
||||
|
||||
// 根据平台类型动态生成第二个标签文本
|
||||
const secondTagText = computed(() => {
|
||||
const gd = props.item.generateData
|
||||
if (props.item.type === 'Music') {
|
||||
if (gd.pureMusic === true) return '纯音乐'
|
||||
if (gd.pureMusic === false) return '有歌词'
|
||||
return ''
|
||||
}
|
||||
// Painting/Video:尝试从 modelParams 中提取比例信息
|
||||
const params = gd.modelParams || {}
|
||||
const key = Object.keys(params).find((k) =>
|
||||
['aspectRatio', 'ratio', 'proportion'].includes(k)
|
||||
)
|
||||
return key ? params[key] : ''
|
||||
})
|
||||
|
||||
const AIbrush = (file, index) => {
|
||||
emit('open-canvas', {
|
||||
mainImage: { url: file, index: index + 1 },
|
||||
|
||||
Loading…
Reference in New Issue
Block a user