diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f22040..b2d901d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ - 点击后会直接跳到 `自动流程 -> 依赖健康 -> Huobao` 卡片,立刻看到当前火山视频配置是否就绪、部署位置和配置提示,不用再自己记 `/settings/ai-config -> 视频 -> 火山引擎` 再手动找入口。 - 同时 `依赖健康` 里的各张集成卡现在都带稳定锚点,后续其他配置提示也可以直接把用户带到最相关的健康卡,而不是只停在说明文字里。 +### AI 视频表单开始跟随视频引擎动态刷新配置提示 + +- `视频引擎` 从 `当前默认引擎` 切到 `Seedance 2.0` 时,`引擎模型` 默认值和占位文案现在会立刻跟着刷新,不用再手动猜当前应该填什么。 +- `Seedance 配置` 提示也会随引擎切换即时更新,表单第一眼就能看出这次走的是默认视频链,还是 `Seedance 2.0 -> 火山视频配置`。 +- 这套联动同样保留“手动改过就不再自动覆盖”的原则,避免把用户已经输入的模型名冲掉。 + ### 修复额度页套餐建议引起的全局渲染报错 - `额度` 页面现在会先初始化 `packageRecommendation` 再渲染套餐建议,不再因为变量未定义把整个工作台渲染链打断。 diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index e29f503..93a65d3 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -12129,6 +12129,38 @@ function renderAiVideoProviderHintHtml(provider = "doubao") { `; } +function bindAiVideoProviderRecommendations(fields, options = {}) { + const providerSelect = fields.querySelector('[data-action-field="videoProvider"]'); + const modelInput = fields.querySelector('[data-action-field="videoModel"]'); + const hintHtml = fields.querySelector('[data-action-field="videoProviderHint"] .sheet-html'); + if (!(providerSelect instanceof HTMLSelectElement)) return; + const seedanceModel = String(options.seedanceModel || "seedance-2.0-pro").trim() || "seedance-2.0-pro"; + if (modelInput instanceof HTMLInputElement) { + modelInput.dataset.recommendationMode = modelInput.dataset.recommendationMode || "auto"; + modelInput.addEventListener("input", () => { + if (modelInput.dataset.recommendationApplying === "1") return; + modelInput.dataset.recommendationMode = "manual"; + }); + } + const sync = () => { + const provider = String(providerSelect.value || "doubao").trim() || "doubao"; + if (hintHtml instanceof HTMLElement) { + hintHtml.innerHTML = renderAiVideoProviderHintHtml(provider); + } + if (modelInput instanceof HTMLInputElement) { + modelInput.placeholder = provider === "seedance2" ? "例如:seedance-2.0-pro" : "留空则沿用当前默认视频模型"; + if (modelInput.dataset.recommendationMode !== "manual") { + modelInput.dataset.recommendationApplying = "1"; + modelInput.value = provider === "seedance2" ? seedanceModel : ""; + modelInput.dataset.recommendationMode = "auto"; + delete modelInput.dataset.recommendationApplying; + } + } + }; + providerSelect.addEventListener("change", sync); + sync(); +} + function openCreateAiVideoAction(defaults = {}) { const guard = getPipelineGuard("aiVideo"); if (!guard.enabled) { @@ -12218,6 +12250,7 @@ function openCreateAiVideoAction(defaults = {}) { ], onOpen: ({ fields }) => { bindCreativeSourceJobRecommendations(fields, { sourceJob, defaultPlatform, titleSuffix: "AI 视频" }); + bindAiVideoProviderRecommendations(fields, { seedanceModel: defaultVideoModel || "seedance-2.0-pro" }); }, onSubmit: async (values) => { if (!values.title?.trim()) throw new Error("请填写任务标题"); diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs index b7dcf08..92265f4 100644 --- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs +++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs @@ -1125,6 +1125,9 @@ test("ai video form explains where Seedance 火山配置 lives", () => { assert.match(APP, /data-action="focus-huobao-video-config"/); assert.match(APP, /id="integration-\$\{escapeHtml\(item\.key\)\}-anchor"/); assert.match(APP, /function focusAutomationHealthWorkspace\(anchorId = "integration-huobao-anchor"\)/); + assert.match(APP, /function bindAiVideoProviderRecommendations\(fields, options = \{\}\)/); + assert.match(APP, /modelInput\.placeholder = provider === "seedance2" \? "例如:seedance-2\.0-pro" : "留空则沿用当前默认视频模型"/); + assert.match(APP, /bindAiVideoProviderRecommendations\(fields, \{ seedanceModel: defaultVideoModel \|\| "seedance-2\.0-pro" \}\)/); assert.match(clickActions, /name === "focus-huobao-video-config"[\s\S]*focusAutomationHealthWorkspace\("integration-huobao-anchor"\)/); });