diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index db38f82..b7fcac6 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -654,6 +654,93 @@ function bindLiveRecorderSheetRecommendations(fields, options = {}) { sync(); } +function bindLiveRecorderSheetRecommendations(fields, options = {}) { + const projectSelect = fields.querySelector('[data-action-field="projectId"]'); + const assistantSelect = fields.querySelector('[data-action-field="assistantId"]'); + const platformSelect = fields.querySelector('[data-action-field="platform"]'); + const titleInput = fields.querySelector('[data-action-field="title"]'); + const rawTextarea = fields.querySelector('[data-action-field="raw"]'); + const contextHtml = fields.querySelector('[data-action-field="context"] .sheet-html'); + const defaultPlatform = options.defaultPlatform || "kuaishou"; + const defaultAssistantId = options.defaultAssistantId || ""; + const keepAutoValue = (field, value) => { + if (!(field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement) || !String(value || "").trim()) return; + field.placeholder = String(value); + if (field.dataset.recommendationMode === "manual") return; + field.dataset.recommendationApplying = "1"; + field.value = String(value); + field.dataset.recommendationMode = "auto"; + delete field.dataset.recommendationApplying; + }; + const bindManualEscape = (field) => { + if (!(field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement)) return; + field.dataset.recommendationMode = field.dataset.recommendationMode || "auto"; + field.addEventListener("input", () => { + if (field.dataset.recommendationApplying === "1") return; + field.dataset.recommendationMode = "manual"; + }); + }; + bindManualEscape(titleInput); + bindManualEscape(rawTextarea); + const getProject = () => { + const projectId = projectSelect instanceof HTMLSelectElement ? projectSelect.value : ""; + return safeArray(appState.dashboard?.projects).find((item) => item.id === projectId) + || getSelectedProject() + || null; + }; + const getPlatform = () => normalizePlatformValue( + platformSelect instanceof HTMLSelectElement ? platformSelect.value : getPreferredPlatform(), + defaultPlatform + ); + const syncAssistantOptions = () => { + if (!(assistantSelect instanceof HTMLSelectElement) || !(projectSelect instanceof HTMLSelectElement)) return; + const currentValue = assistantSelect.value || ""; + const assistants = getAssistantOptions(projectSelect.value || ""); + const fallbackValue = assistants.some((item) => item.value === currentValue) + ? currentValue + : assistants.some((item) => item.value === defaultAssistantId) + ? defaultAssistantId + : assistants[0]?.value || ""; + assistantSelect.innerHTML = [{ value: "", label: "暂不绑定" }, ...assistants].map((item) => ` + + `).join(""); + assistantSelect.value = fallbackValue; + }; + const syncContext = () => { + if (!(contextHtml instanceof HTMLElement)) return; + const project = getProject(); + const assistantId = assistantSelect instanceof HTMLSelectElement ? assistantSelect.value : defaultAssistantId; + contextHtml.innerHTML = renderIntakeActionContextHtml(project?.id || "", assistantId); + }; + const syncTitle = () => { + const project = getProject(); + const platform = getPlatform(); + const recommended = recommendLiveRecorderTitle(project, platform); + if (titleInput instanceof HTMLInputElement) { + keepAutoValue(titleInput, recommended); + } + }; + const syncRawSamples = () => { + if (!(rawTextarea instanceof HTMLTextAreaElement)) return; + keepAutoValue(rawTextarea, recommendLiveRecorderImportSamples(getPlatform())); + }; + const syncDescription = () => { + if (!(options.description instanceof HTMLElement)) return; + options.description.textContent = `按行粘贴${platformLabel(getPlatform())}直播源,支持用逗号附带清晰度和标题,注释行会被视为停用源。`; + }; + const sync = () => { + syncAssistantOptions(); + syncContext(); + syncTitle(); + syncRawSamples(); + syncDescription(); + }; + projectSelect?.addEventListener("change", sync); + assistantSelect?.addEventListener("change", syncContext); + platformSelect?.addEventListener("change", sync); + sync(); +} + function formatDateTime(value) { if (!value) return "-"; const date = new Date(value); @@ -11949,7 +12036,7 @@ function openLiveRecorderCreateAction() { { name: "autoStart", label: "导入后立即开始", type: "checkbox", value: true } ], onOpen: ({ fields }) => { - bindLiveRecorderSheetRecommendations(fields, { defaultPlatform }); + bindLiveRecorderSheetRecommendations(fields, { defaultPlatform, defaultAssistantId }); }, onSubmit: async (values) => { if (!values.sourceUrl?.trim()) throw new Error("请填写直播源链接"); @@ -12021,7 +12108,10 @@ function openLiveRecorderSourceAction(sourceId) { { name: "enabled", label: "启用录制源", type: "checkbox", value: Boolean(source.enabled) } ], onOpen: ({ fields }) => { - bindLiveRecorderSheetRecommendations(fields, { defaultPlatform: source.platform || "kuaishou" }); + bindLiveRecorderSheetRecommendations(fields, { + defaultPlatform: source.platform || "kuaishou", + defaultAssistantId + }); }, onSubmit: async (values) => { const saved = await storyforgeFetch(`/v2/live-recorder/sources/${encodeURIComponent(source.id)}`, { @@ -12064,18 +12154,7 @@ function openLiveRecorderImportAction() { } ], onOpen: ({ fields, description }) => { - bindLiveRecorderSheetRecommendations(fields, { defaultPlatform }); - const platformSelect = fields.querySelector('[data-action-field="platform"]'); - const syncDescription = () => { - if (!(description instanceof HTMLElement)) return; - const platform = normalizePlatformValue( - platformSelect instanceof HTMLSelectElement ? platformSelect.value : getPreferredPlatform(), - defaultPlatform - ); - description.textContent = `按行粘贴${platformLabel(platform)}直播源,支持用逗号附带清晰度和标题,注释行会被视为停用源。`; - }; - platformSelect?.addEventListener("change", syncDescription); - syncDescription(); + bindLiveRecorderSheetRecommendations(fields, { defaultPlatform, description }); }, onSubmit: async (values) => { if (!String(values.raw || "").trim()) throw new Error("请先粘贴配置文本");