diff --git a/README.md b/README.md index 06de6d1..f788d4d 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ Oxfmt 不负责代码质量,Oxlint 不负责 Vue 的完整类型推导;Vue S - 外层应用固定为视口高度;侧栏、顶栏、项目标题和工作流导航不会随正文滚动。 - 工作流只保留左侧导航,项目标题下不再重复展示相同标签。主体身份、分镜设计、镜头生产和图库采用内容优先布局:常用筛选/剧集选择/刷新在固定的紧凑工具栏,列表占据剩余高度。 -- 项目标题栏只显示返回入口和名称,不再放置状态标签与刷新按钮;项目仍每 6 秒自动刷新,读取失败时在错误提示内提供重试入口。 +- 项目标题栏只显示返回入口和名称,不再放置状态标签与刷新按钮;除形态图库使用手动刷新外,其他项目工作区每 6 秒自动刷新,读取失败时在错误提示内提供重试入口。 - 只有当前项目正式状态为 `completed` 才解锁下游工作区;草稿、生成中、待审核、失败和读取期间默认禁用下游导航。直接访问下游链接只显示返回剧本创作的引导,不挂载操作组件;轮询确认完成后自动解锁,切换项目立即重新判定。页面内提交守卫同步检查完成状态;这是前端交互限制,不代替后端权限与业务校验。 - 项目列表采用固定表头表格;剧本、分镜、身份与生产页的目录和详情独立滚动,其他工作区只滚动内容区。 - “我的剧本”状态筛选采用中号控件,与搜索框等高并扩大点击区域;刷新、主题、折叠和关闭等单图标按钮统一使用直角正方形,加载时保持尺寸不变,保留可访问名称。 @@ -199,7 +199,7 @@ API 层也提供 `state` 与 `breakdown/latest` 方法。页面通过共享 chec 1. `POST /projects` 返回顶层 `202 { projectId, status }`,其他接口通常返回 `{ data }`;HTTP 层分别兼容。 2. Breakdown、Storyboard 和恢复接口目前是**长 HTTP 请求**。不设客户端短超时,不自动重试 POST。关闭页面只会丢失请求连接,**不会取消后端任务**。 -3. 项目列表每 12 秒、项目详情每 6 秒串行刷新。隐藏页面暂停自动查询。切换项目或离开布局时取消查询,旧响应不会覆盖新项目。 +3. 项目列表每 12 秒、项目详情每 6 秒串行刷新。形态图库暂停父布局和页面定时查询,返回其他工作区恢复轮询;图库直接链接被未完成状态拦截时可手动刷新项目状态。隐藏页面暂停自动查询。切换项目或离开布局时取消查询,旧响应不会覆盖新项目。 4. 抽取任务进度不是全流程进度。抽取完成之后还需要主体合并、形态、节拍、镜头和持久化。 5. 后端失败 checkpoint 可能只有错误。页面回看最近可用的完整快照以保留成果,同时使用最新记录的 execution 状态。显示的是**最近可用快照**,并不保证全部成果来自最后一次执行。 6. 后端并不为每个阶段提供持久化的 running 状态。没有明确终态时,页面显示“阶段快照 · 执行状态待确认”,不会猜测成功或失败。 @@ -227,7 +227,7 @@ API 层也提供 `state` 与 `breakdown/latest` 方法。页面通过共享 chec 4. 单图宽高同时留空使用后端默认 2K,自定义时必须同时提供正整数,具体尺寸限制仍由模型校验。当前 后端图片模型 Provider 没有使用 negativePrompt,所以页面不提供无效的负向提示词输入。 5. 单图生成新增历史记录,不删除旧图。无主图时默认勾选“生成成功后设为主图”;已有主图时默认不替换。候选图可以在历史弹窗二次确认后通过 PUT 设为主图,只允许已完成图片。 6. 批量默认跳过已有主图的形态;勾选“已有主图也新增候选图”发送 `force: true`。后端批量 force 只生成新图,**不会替换已有主图**。批量作用于整个项目,不受类型/文字/缺图筛选影响,也不接收单图的尺寸与自定义 Prompt。 -7. 图库每 6 秒读取一次真实记录,弹窗只在打开时查询该形态图片。数据库存在 pending / generating 时暂停再次生图;网络错误不自动重试有费用的请求。HTTP 200 中的批量部分失败单独展示。 +7. 图库、镜头影响检查、关联素材和图片历史均不定时刷新;首次进入、切换查询目标、手动刷新及本次写操作完成后按需读取。“刷新图库”同时更新项目状态。数据库存在 pending / generating 时暂停再次生图;网络错误不自动重试有费用的请求。HTTP 200 中的批量部分失败单独展示。当前形态列表接口返回完整数组,无分页参数或游标,因此不显示分页或触底加载入口。筛选栏与有值的关联素材行共用一个不透明背景的吸顶容器,内部无 margin 断层;关联素材保持单行横向滚动、上下居中,图片区域沿正文向下滚动。 8. 图片使用持久化后的 `/storage/...` 地址,开发代理已配置。生产环境也必须代理 `/storage/`,不能让 SPA 回退返回 index.html。无效 URL、加载失败、无图和后端生成失败有各自提示;不会自动使用可能过期的 Provider 临时地址。 9. 设为主图后,回到分镜点击“刷新参考图”读取最新绑定。已有视频提示词不会自动重生成,需要按需覆盖。本页面不会调用 `/production/start` 或实际视频生成。 10. 人物、场景、道具均仅继承已锁定身份的当前母版;无母版或未锁定不继承。刷新过期形态只生成候选,验图后手动切换主图。批量图片可限制本次数量,回执独立展示因上限未执行数,不把它算作已有结果的跳过数。 diff --git a/src/App.vue b/src/App.vue index a04de8e..975d700 100644 --- a/src/App.vue +++ b/src/App.vue @@ -170,7 +170,7 @@ const menuOptions = computed(() => [
生产环境
为 /api、/storage 配置反向代理,并为长工作流设置足够的超时。
状态更新
-
项目详情每 6 秒刷新;关闭页面不会取消后端任务。
+
形态图库使用手动刷新,其他项目工作区每 6 秒更新;关闭页面不会取消后端任务。
diff --git a/src/admin.css b/src/admin.css index e1441eb..16d9351 100644 --- a/src/admin.css +++ b/src/admin.css @@ -458,13 +458,16 @@ body { .gallery-workspace-page .workspace-scroll-content { padding-top: 8px; } -/* 图库只使用正文纵向滚动;筛选栏在同一滚动上下文吸顶,不再被头部高度限制。 */ -.gallery-workspace-page .form-image-filter-region { +/* 筛选与关联素材整体吸顶;间距由内部 padding 承担,避免出现透明断层。 */ +.gallery-workspace-page .gallery-sticky-controls { position: sticky; top: 0; z-index: 10; background: var(--app-subtle); - margin-block: 10px 12px; + margin-block: 10px 0; +} +.gallery-sticky-controls > .form-image-filter-region { + margin-block: 0; } .gallery-workspace-page .workspace-scroll > .n-scrollbar-container { overflow-anchor: none; @@ -1511,7 +1514,7 @@ body { gap: 10px; padding: 14px 16px; background: var(--app-subtle); - margin-bottom: 12px; + margin: 0; min-width: 0; } /* 关联素材始终单行横向浏览,数量和长名称不会撑高定位区或压缩图片区域。 */ @@ -1526,7 +1529,8 @@ body { gap: 12px; width: max-content; white-space: nowrap; - padding-bottom: 8px; + min-height: 32px; + padding-block: 6px; } .asset-impact-links > * { flex-shrink: 0; diff --git a/src/components/ui/flatTheme.test.ts b/src/components/ui/flatTheme.test.ts index 978ec08..e9d9276 100644 --- a/src/components/ui/flatTheme.test.ts +++ b/src/components/ui/flatTheme.test.ts @@ -145,12 +145,16 @@ describe('全站平面主题', () => { // 静态保护吸顶和横向尺寸规则;真实视口滚动与位置仍需浏览器验收。 const css = readFileSync('src/admin.css', 'utf8') expect(css).toMatch( - /\.gallery-workspace-page \.form-image-filter-region\s*\{[^}]*position:\s*sticky;[^}]*top:\s*0;[^}]*z-index:\s*10;[^}]*background:\s*var\(--app-subtle\);/ + /\.gallery-workspace-page \.gallery-sticky-controls\s*\{[^}]*position:\s*sticky;[^}]*top:\s*0;[^}]*z-index:\s*10;[^}]*background:\s*var\(--app-subtle\);/ ) expect(css).toMatch( /\.gallery-workspace-page \.workspace-scroll > \.n-scrollbar-container\s*\{[^}]*overflow-anchor:\s*none;/ ) expect(css).toMatch(/\.asset-impact-links\s*\{[^}]*width:\s*max-content;[^}]*white-space:\s*nowrap;/) + expect(css).toMatch(/\.gallery-sticky-controls > \.form-image-filter-region\s*\{[^}]*margin-block:\s*0;/) + expect(css).toMatch( + /\.asset-impact-links\s*\{[^}]*align-items:\s*center;[^}]*min-height:\s*32px;[^}]*padding-block:\s*6px;/ + ) expect(css).toMatch(/\.asset-impact-links-scroll\.n-scrollbar\s*\{[^}]*height:\s*auto;[^}]*max-width:\s*100%;/) expect(readFileSync('src/features/subject-images/SubjectImagesPage.vue', 'utf8')).toMatch( /]*>\s*