feat: highlight intake and recorder status in production
Some checks failed
StoryForge CI / Baseline checks (push) Has been cancelled
StoryForge CI / Backend tests (push) Has been cancelled
StoryForge CI / Web tests (push) Has been cancelled

This commit is contained in:
kris
2026-04-07 16:23:28 +08:00
parent 4cd9ff77d9
commit 07680dce4f
3 changed files with 28 additions and 14 deletions

View File

@@ -689,3 +689,4 @@
- 首页 `1 主 2 次` 动作里把 `视频录制` 抬成了高频次级动作,当前项目有生产任务时能更快进入录制维护入口。
- `AI 视频` 表单开始直接显示“当前项目最近使用的视频引擎”,像 `Seedance 2.0 · seedance-2.0-pro` 这类信息会在打开表单时直接可见,并保留跳到火山配置状态的入口。
- `生产中心` 现在把 `导入主页 / 导入作品 / 导入文本 / 上传视频` 这批接入入口统一接进了顶部动作区和生产队列工作流卡,不用离开生产中心也能开始接入素材。
- `生产中心``生产队列` 首屏现在会直接显示 `接入与录制` 概况:最近内容源同步条数、录制源数量、录制服务状态,以及一组直达 `导入主页 / 导入作品 / 视频录制` 的动作。

View File

@@ -7559,7 +7559,7 @@ function renderPlaybookScreen() {
);
}
function renderProductionMobileTaskDeck({ activeTab, activeJobs, failedJobs, recoverableCount, works, recentDocs }) {
function renderProductionMobileTaskDeck({ activeTab, activeJobs, failedJobs, recoverableCount, works, recentDocs, contentSyncCount = 0, recorderSourceCount = 0, recorderRunning = false }) {
const taskCards = [];
if (activeTab === "recovery") {
const nextRecoverable = failedJobs.find((item) => item.recovery.recoverable) || null;
@@ -7663,18 +7663,17 @@ function renderProductionMobileTaskDeck({ activeTab, activeJobs, failedJobs, rec
</div>
</div>
`);
if (topJob) {
taskCards.push(`
<div class="task-item compact">
<h4>${escapeHtml(topJob.title)}</h4>
<p>${escapeHtml(brief(topJob.style_summary || topJob.transcript_text || topJob.error || "暂无摘要", 84))}</p>
<div class="task-meta">
<span class="tag ${statusTone(topJob.status)}">${escapeHtml(topJob.status)}</span>
<span class="tag">${escapeHtml(topJob.line_type || "analysis")}</span>
</div>
taskCards.push(`
<div class="task-item compact">
<h4>接入与录制</h4>
<p>${escapeHtml(`最近内容源同步 ${formatNumber(contentSyncCount)} 条 · 录制源 ${formatNumber(recorderSourceCount)}${recorderRunning ? ",录制服务正在运行。" : ",录制服务待确认。"} `)}</p>
<div class="task-meta">
${actionTag("导入主页", "open-import-homepage")}
${actionTag("导入作品", "open-import-video-link")}
${actionTag("视频录制", "focus-live-recorder-maintenance")}
</div>
`);
}
</div>
`);
}
return `
<div class="mobile-only production-mobile-task-deck">
@@ -7696,6 +7695,9 @@ function renderProductionScreen() {
const recoverableCount = failedJobs.filter((item) => item.recovery.recoverable).length;
const recentDocs = appState.documents.slice(0, 3);
const works = getProductionWorks(6);
const contentSyncCount = jobs.filter((item) => item.line_type === "content_source_sync").length;
const recorderSourceCount = safeArray(appState.liveRecorderSources).length;
const recorderRunning = Boolean(appState.liveRecorderStatus?.running);
const isMobileUi = isMobileViewport();
const productionHandoffAttrs = buildMainAgentHandoffAttrs({
sourceScreen: "production",
@@ -7735,12 +7737,13 @@ function renderProductionScreen() {
<div class="queue-card"><h4>分析任务</h4><p> ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "analysis").length))} </p></div>
<div class="queue-card"><h4>实拍剪辑</h4><p> ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "real_cut").length))} </p></div>
<div class="queue-card"><h4>AI 视频</h4><p> ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "ai_video").length))} </p></div>
<div class="queue-card"><h4>内容源同步</h4><p> ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "content_source_sync").length))} </p></div>
<div class="queue-card"><h4>接入与录制</h4><p> ${escapeHtml(formatNumber(contentSyncCount))} · ${escapeHtml(formatNumber(recorderSourceCount))} </p></div>
</div>
<div class="mobile-only compact-summary-row" style="margin-top:14px;">
<span class="tag blue">分析 ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "analysis").length))}</span>
<span class="tag">实拍 ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "real_cut").length))}</span>
<span class="tag green">AI 视频 ${escapeHtml(formatNumber(jobs.filter((item) => item.line_type === "ai_video").length))}</span>
<span class="tag ${recorderRunning ? "green" : "orange"}">录制 ${escapeHtml(formatNumber(recorderSourceCount))}</span>
</div>
</div>
${renderQuotaBlockingNotice()}
@@ -7788,7 +7791,10 @@ function renderProductionScreen() {
failedJobs,
recoverableCount,
works,
recentDocs
recentDocs,
contentSyncCount,
recorderSourceCount,
recorderRunning
})}
${renderDetailTabs("productionDetailTab", tabs)}
${activeTab === "queue" ? `

View File

@@ -496,12 +496,19 @@ test("mobile discovery and production simplify duplicated top-level actions", ()
test("production queue promotes intake entrypoints so import flows are reachable without leaving production", () => {
const production = extractBetween(APP, "function renderProductionScreen()", "function renderReviewScreen()");
const mobileDeck = extractBetween(APP, "function renderProductionMobileTaskDeck(", "function renderProductionScreen()");
assert.match(production, /const intakeEntryActionsHtml =/);
assert.match(production, /<h4>接入素材<\/h4>/);
assert.match(production, /把主页、作品链接、文本素材或本地视频先接进项目/);
assert.match(production, /先从导入主页、作品、文本或上传视频开始接入素材/);
assert.match(production, /<h4>接入与录制<\/h4>/);
assert.match(production, /同步 .* 条 · 录制源 .* 个/);
assert.match(production, /录制 \${escapeHtml\(formatNumber\(recorderSourceCount\)\)}/);
assert.match(production, /actionTag\("导入作品", "open-import-video-link"\)/);
assert.match(production, /actionTag\("上传视频", "open-upload-video"\)/);
assert.match(mobileDeck, /<h4>接入与录制<\/h4>/);
assert.match(mobileDeck, /actionTag\("导入主页", "open-import-homepage"\)/);
assert.match(mobileDeck, /actionTag\("视频录制", "focus-live-recorder-maintenance"\)/);
});
test("discovery page promotes selected-account actions into direct execute flows", () => {