diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index f9521b9..4d584c6 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -5477,6 +5477,18 @@ function renderDiscoveryScreen() { ${escapeHtml(getAccountName(selected) || "未选中")} +
+
+ 下一步先做 + ${escapeHtml(detailTabs.find((tab) => tab.value === activeTab)?.label || "账号概览")} +
+

${escapeHtml(selected ? `先围绕 ${getAccountName(selected)} 做导入、分析和相似扩展。` : "先从账号池里选一个对象,再继续导入和分析。")}

+
+ ${actionTag("导入当前对标", "open-import-selected-account")} + ${actionTag("账号分析", "analyze-selected-account")} + ${actionTag("查相似", "open-similar-search")} +
+
当前对标 ${escapeHtml(getAccountName(selected) || "未选中")} ${escapeHtml(importedSources.length ? `已接入 ${importedSources.length}` : "未接入项目")} @@ -6043,6 +6055,31 @@ function renderProductionScreen() {
把队列、恢复、录制和产物拆开看,减少一次性信息量。
+
+
+ 当前工作流 + ${escapeHtml(tabs.find((tab) => tab.value === activeTab)?.label || "生产队列")} +
+

${escapeHtml( + activeTab === "recovery" + ? "先处理失败任务和可恢复项,再决定是否批量重开。" + : activeTab === "recorder" + ? "先确认录制服务和文件状态,再回到队列继续推进。" + : activeTab === "outputs" + ? "先看产物和作品,再决定是否回到复盘或继续生产。" + : "先看处理中任务,再把异常和产物安排到下一步。" + )}

+
+ ${activeTab === "recovery" + ? `${actionTag("批量恢复", "batch-recover-jobs")} ${actionTag("查看恢复记录", "select-page-tab", `data-page-tab-key="productionDetailTab" data-page-tab-value="recovery"`)}` + : activeTab === "outputs" + ? `${actionTag("去复盘", "goto-review")} ${actionTag("查看产物", "select-page-tab", `data-page-tab-key="productionDetailTab" data-page-tab-value="outputs"`)}` + : activeTab === "recorder" + ? `${actionTag("录制维护", "select-page-tab", `data-page-tab-key="productionDetailTab" data-page-tab-value="recorder"`)} ${actionTag("交给主 Agent", "handoff-to-main-agent", productionHandoffAttrs)}` + : `${actionTag("批量恢复", "batch-recover-jobs")} ${actionTag("交给主 Agent", "handoff-to-main-agent", productionHandoffAttrs)}` + } +
+
处理中 ${escapeHtml(formatNumber(activeJobs.length || jobs.filter((item) => item.status !== "completed").length))} 失败 ${escapeHtml(formatNumber(failedJobs.length))} diff --git a/web/storyforge-web-v4/assets/styles.css b/web/storyforge-web-v4/assets/styles.css index d0fca0d..5f65c16 100644 --- a/web/storyforge-web-v4/assets/styles.css +++ b/web/storyforge-web-v4/assets/styles.css @@ -1386,6 +1386,10 @@ select { gap: 8px; } +.mobile-flow-focus-card { + display: none; +} + .sheet-html { border: 1px solid var(--line); border-radius: 16px; @@ -2189,6 +2193,42 @@ tbody tr:hover { text-align: center; } + .mobile-flow-focus-card { + display: grid; + gap: 10px; + margin-bottom: 14px; + padding: 13px 14px; + border: 1px solid rgba(79, 143, 238, 0.16); + border-radius: 16px; + background: linear-gradient(180deg, rgba(247, 251, 255, 0.98) 0%, rgba(238, 246, 255, 0.98) 100%); + } + + .mobile-flow-focus-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + } + + .mobile-flow-focus-head strong { + font-size: 13px; + line-height: 1.3; + color: var(--text); + } + + .mobile-flow-focus-card p { + margin: 0; + font-size: 12px; + line-height: 1.55; + color: var(--muted); + } + + .mobile-flow-focus-card .task-meta .tag { + flex: 1 1 calc(50% - 4px); + justify-content: center; + text-align: center; + } + .entity-cell { align-items: flex-start; } @@ -2364,6 +2404,42 @@ tbody tr:hover { text-align: center; } + .mobile-flow-focus-card { + display: grid; + gap: 10px; + margin-bottom: 14px; + padding: 13px 14px; + border: 1px solid rgba(79, 143, 238, 0.16); + border-radius: 16px; + background: linear-gradient(180deg, rgba(247, 251, 255, 0.98) 0%, rgba(238, 246, 255, 0.98) 100%); + } + + .mobile-flow-focus-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + } + + .mobile-flow-focus-head strong { + font-size: 13px; + line-height: 1.3; + color: var(--text); + } + + .mobile-flow-focus-card p { + margin: 0; + font-size: 12px; + line-height: 1.55; + color: var(--muted); + } + + .mobile-flow-focus-card .task-meta .tag { + flex: 1 1 calc(50% - 4px); + justify-content: center; + text-align: center; + } + .integration-card-head { flex-direction: column; } diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs index 0a25710..9b8f3dc 100644 --- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs +++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs @@ -135,6 +135,21 @@ test("discovery and production screens expose compact mobile flow summaries", () assert.match(production, /失败/); }); +test("discovery and production screens expose mobile focus cards with next-step actions", () => { + const discovery = extractBetween(APP, "function renderDiscoveryScreen()", "function renderTrackingScreen()"); + const production = extractBetween(APP, "function renderProductionScreen()", "function renderReviewScreen()"); + const cssMobile = extractBetween(CSS, "@media (max-width: 760px) {", "@media (max-width: 560px) {"); + + assert.match(discovery, /mobile-only mobile-flow-focus-card/); + assert.match(discovery, /下一步先做/); + assert.match(discovery, /导入当前对标/); + assert.match(discovery, /查相似/); + assert.match(production, /mobile-only mobile-flow-focus-card/); + assert.match(production, /当前工作流/); + assert.match(production, /批量恢复/); + assert.match(cssMobile, /\.mobile-flow-focus-card/); +}); + test("projects screen uses an adaptive project grid instead of a fixed three-column squeeze", () => { const projects = extractBetween(APP, "function renderProjectsScreen()", "function getActiveDetailTab("); assert.match(projects, /project-status-grid/);