音效元素的时长选择换成滑动条,和视频元素的形式保持一致

将音效时长弹层从独立秒数按钮改为 2-10 秒滑动条。

复用视频时长滑动条样式并移除旧音效按钮网格样式。

更新音效时长控件测试和音乐生成入口设计文档。
This commit is contained in:
2026-06-22 19:20:34 +08:00
parent e91a46a419
commit 6e475af269
4 changed files with 33 additions and 26 deletions
@@ -22,7 +22,7 @@
- `prompt`:用户输入的音效提示词。前端与 BFF 对内仍使用 `prompt`,提交到 VectorEngine Vidu 时同时写入 `prompt` 与 `sound` 同值,兼容线上网关对 `sound` 字段的反序列化要求。
- `model`:固定默认 `audio1.0`,UI 以禁用态模型胶囊显示为 `Vidu`,位置紧贴生成按钮;暂不展示 Suno 文生音效模型入口。
- `duration`:Vidu 音频时长,UI 以一个无标题参数按钮展示,选项范围 `2-10` 秒,默认 `5` 秒。
- `duration`:Vidu 音频时长,UI 以一个无标题参数按钮展示当前秒数,点击后展开与视频时长一致的滑动条,范围 `2-10` 秒,步进 `1` 秒,默认 `5` 秒。
- 提交到 VectorEngine 时映射为 Vidu 文生音频字段:`model: "audio1.0"`、`prompt`、`sound: prompt`、`duration`、可选 `seed`。当前编辑器音效固定使用 Vidu `audio1.0`,不再走 Suno `task: "sound"` / `metadata_params` 文生音效契约。
### 生成游戏背景音乐
@@ -119,7 +119,7 @@ POST /api/editor/audios/background-music/generations
- 底部工具栏显示 `生成音乐`。
- 点击 `生成音乐` 只出现选项框,不立刻创建占位。
- 点击 `生成游戏音效` 后出现音效面板,文本字段为 `prompt`;底部左侧只有一个无标题时长参数按钮,右侧为固定 `Vidu` 模型胶囊和生成按钮。
- 音效时长只提供 `2-10` 秒选项,默认 `5` 秒,提交到 BFF 的字段为 `duration`。
- 音效时长使用滑动条选择 `2-10` 秒,步进 `1` 秒,默认 `5` 秒,提交到 BFF 的字段为 `duration`。
- 音效面板模型显示 `Vidu`,提交 `model: "audio1.0"`;后端转发到 Vidu 时同时携带 `prompt` 和 `sound`;不显示 Suno 文生音效模型或 Suno 音效入口。
- 点击 `生成游戏背景音乐` 后出现背景音乐面板,字段为 `gpt_description_prompt`,右侧固定显示 `Suno` 模型胶囊,不展示 `make_instrumental`。
- 音效提交到 `/api/editor/audios/sound-effects/generations`,背景音乐提交到 `/api/editor/audios/background-music/generations`。
@@ -631,14 +631,18 @@ describe('ImageCanvasGenerationComposerView', () => {
within(panel).getByRole('button', { name: '音效时长 5秒' }),
);
const optionPanel = screen.getByRole('menu', { name: '音效时长选项' });
const durationSlider = within(optionPanel).getByRole('slider', {
name: '音效时长',
}) as HTMLInputElement;
expect(durationSlider.min).toBe('2');
expect(durationSlider.max).toBe('10');
expect(durationSlider.step).toBe('1');
expect(durationSlider.value).toBe('5');
expect(
within(optionPanel).getByRole('button', { name: '时长 2秒' }),
).toBeTruthy();
expect(
within(optionPanel).getByRole('button', { name: '时长 10秒' }),
).toBeTruthy();
within(optionPanel).queryByRole('button', { name: '时长 2秒' }),
).toBeNull();
expect(within(optionPanel).queryByText(/BPM/u)).toBeNull();
fireEvent.click(within(optionPanel).getByRole('button', { name: '时长 8秒' }));
fireEvent.change(durationSlider, { target: { value: '8' } });
expect(screen.getByLabelText('当前音效时长').textContent).toBe('8');
expect(
@@ -47,7 +47,6 @@ import {
DEFAULT_VIDEO_WEB_SEARCH_ENABLED,
EDITOR_VIDEO_ASPECT_RATIO_OPTIONS,
EDITOR_SOUND_EFFECT_MODEL_OPTIONS,
SOUND_EFFECT_DURATION_OPTIONS,
EDITOR_VIDEO_MODEL_OPTIONS,
EDITOR_VIDEO_RESOLUTION_OPTIONS,
VIDEO_MODEL_SEEDANCE_2_FAST,
@@ -817,18 +816,27 @@ function ImageCanvasAudioGenerationComposerView({
'above',
)}
>
<div className="image-canvas-editor__option-popover-items image-canvas-editor__audio-type-options">
{SOUND_EFFECT_DURATION_OPTIONS.map((option) => (
<VideoOptionChoice
key={option}
selected={soundDuration === option}
disabled={isGenerating}
ariaLabel={`时长 ${option}秒`}
onClick={() => updateSoundDuration(option)}
>
{option}秒
</VideoOptionChoice>
))}
<div className="image-canvas-editor__option-popover-sections">
<section className="image-canvas-editor__option-popover-section">
<span className="image-canvas-editor__option-popover-title">
时长
</span>
<div className="image-canvas-editor__video-duration-slider">
<input
type="range"
min={2}
max={10}
step={1}
value={soundDuration}
disabled={isGenerating}
aria-label="音效时长"
onChange={(event) =>
updateSoundDuration(Number(event.target.value))
}
/>
<span>{soundDuration}秒</span>
</div>
</section>
</div>
</PlatformFloatingMenu>,
)
-5
View File
@@ -6714,11 +6714,6 @@ button.image-canvas-editor__reference-chip:disabled {
padding: 0.88rem;
}
.image-canvas-editor__audio-type-options {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.image-canvas-editor__audio-bpm-control {
display: grid;
grid-template-columns: minmax(7rem, 1fr) 4.7rem;