diff --git a/README.md b/README.md index 4327e99..7393a20 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Oxfmt 不负责代码质量,Oxlint 不负责 Vue 的完整类型推导;Vue S - 拆解人物、场景、道具共用固定斑马纹列表:奇数行常驻原悬停灰底,偶数行保留次级底色,整行不随鼠标或键盘焦点变色;内部控件仍保留交互与焦点反馈,搜索后的条纹顺序按可见列表重新计算。此类记录(含剧本角色设定)首项同样保留 20px 内边距,主题切换时背景色使用 160ms 过渡,并遵循系统减少动态效果设置。 - 拆解主体列表的搜索框统一靠左,图标使用 `NInput` 的 `prefix` 插槽;统计紧随输入框弱化显示,搜索时显示匹配数/总数,清空后恢复总数。搜索与统计垂直居中,窄内容区允许换行,不再左右分散。 - 展开后的每个形态使用独立直角色块,与所在主体条纹采用相反的主题灰阶;保留 16px 内边距、12px 块间距和名称/默认标签间距,奇偶行及搜索重排后均可区分内外层,不新增悬停变色。 +- 拆解设置中“每组集数”和“抽取模块”使用相同标题间距;输入框、复选框与预览按钮对齐到 34px 控件行,模块说明独立放在下方。“集 / 组”保持单行,窄抽屉按实际内容宽度将操作和完整模块分组换行,校验与预览逻辑不变。 - 窄屏默认收起侧栏,目录与正文改用紧凑布局;执行记录仍可查看。长弹窗在自身内部滚动。 布局样式集中在 `src/admin.css`,业务排版保留在 `src/styles.css`。新增页面应使用 `WorkspacePage`(内容优先页开启 `compact`),低频操作使用 `WorkspaceTools`;避免通过 `body` 或 `window.scrollTo` 管理滚动。 diff --git a/src/admin.css b/src/admin.css index 598fcc1..9221e43 100644 --- a/src/admin.css +++ b/src/admin.css @@ -343,6 +343,86 @@ body { .control-row-checkbox .n-checkbox-box-wrapper { align-self: center; } +/* 拆解配置跟随抽屉实际宽度;标题 20px + 间距 8px,下方统一使用 34px 控件行。 */ +.breakdown-settings-panel { + container: breakdown-settings / inline-size; +} +.breakdown-config { + --breakdown-label-offset: 28px; + display: grid; + grid-template-columns: 180px minmax(0, 1fr) auto; + align-items: start; + gap: 16px 24px; +} +.breakdown-config .field-label { + margin: 0 0 8px; + padding: 0; + line-height: 20px; +} +.breakdown-group-field, +.breakdown-modules-field, +.breakdown-module-option { + min-width: 0; +} +.breakdown-group-input { + display: flex; + align-items: center; + gap: 12px; +} +.breakdown-group-number.n-input-number { + flex: 0 0 120px; + width: 120px; +} +.breakdown-group-unit { + flex-shrink: 0; + white-space: nowrap; + color: var(--app-muted); + font-size: 14px; +} +.breakdown-modules-field { + margin: 0; + padding: 0; + border: 0; +} +.breakdown-module-options { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px 16px; +} +.breakdown-module-description { + margin: 4px 0 0; + padding-left: 24px; + color: var(--app-muted); + font-size: 11px; + line-height: 18px; + overflow-wrap: anywhere; +} +.breakdown-preview-button.n-button { + align-self: start; + margin-top: var(--breakdown-label-offset); + white-space: nowrap; +} +@container breakdown-settings (max-width: 780px) { + .breakdown-config { + grid-template-columns: 180px minmax(0, 1fr); + } + .breakdown-preview-button.n-button { + grid-column: 2; + justify-self: end; + margin-top: 0; + } +} +@container breakdown-settings (max-width: 560px) { + .breakdown-config { + grid-template-columns: minmax(0, 1fr); + } + .breakdown-module-options { + grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)); + } + .breakdown-preview-button.n-button { + grid-column: 1; + } +} .workspace-tools-body .storyboard-coverage { margin-block: 20px; } diff --git a/src/features/breakdown/BreakdownPage.test.ts b/src/features/breakdown/BreakdownPage.test.ts index d54f464..5d16167 100644 --- a/src/features/breakdown/BreakdownPage.test.ts +++ b/src/features/breakdown/BreakdownPage.test.ts @@ -140,6 +140,58 @@ async function selectTab(label: string) { } describe('拆解页内容滚动', () => { + it('拆解设置保留输入标签、单行单位与模块说明,改版后仍校验模块和剧本状态', async () => { + const fetcher = vi.fn() + vi.stubGlobal('fetch', fetcher) + const provided = mountPage() + await wrapper!.get('[data-workspace-tools]').trigger('click') + await flushPromises() + const config = wrapper!.get('.breakdown-config') + expect(config.get('label[for="group-size"]').text()).toBe('每组集数') + expect(config.get('#group-size').element.value).toBe('3') + expect(config.get('.breakdown-group-unit').text()).toBe('集 / 组') + expect(config.get('fieldset > legend').text()).toBe('抽取模块') + const options = config.findAll('.breakdown-module-option') + expect(options).toHaveLength(3) + const preview = config.get('.breakdown-preview-button') + expect(preview.element.disabled).toBe(false) + for (const option of options) { + const checkbox = option.get('[role="checkbox"]') + const description = option.get('.breakdown-module-description') + expect(checkbox.attributes('aria-describedby')).toBe(description.attributes('id')) + expect(checkbox.find('.breakdown-module-description').exists()).toBe(false) + expect(description.text()).not.toBe('') + await checkbox.trigger('click') + } + expect(preview.element.disabled).toBe(true) + expect(wrapper!.text()).toContain('至少选择一个抽取模块') + await options[1]!.get('[role="checkbox"]').trigger('click') + expect(preview.element.disabled).toBe(false) + provided.data.value!.project.status = 'generating' + await flushPromises() + expect(preview.element.disabled).toBe(true) + expect(fetcher).not.toHaveBeenCalled() + }) + + it('拆解配置按抽屉宽度换行,单位不收缩,三类控件对齐且说明位于控件下方', () => { + // DOM 环境不计算坐标,检查统一标题偏移、34px 控件行和容器断点契约。 + const css = readFileSync('src/admin.css', 'utf8') + expect(css).toMatch(/\.breakdown-settings-panel\s*\{\s*container:\s*breakdown-settings \/ inline-size;/) + expect(css).toMatch(/\.breakdown-config\s*\{[^}]*--breakdown-label-offset:\s*28px;/) + expect(css).toMatch(/\.breakdown-config \.field-label\s*\{[^}]*margin:\s*0 0 8px;[^}]*line-height:\s*20px;/) + expect(css).toMatch(/\.breakdown-group-unit\s*\{[^}]*flex-shrink:\s*0;[^}]*white-space:\s*nowrap;/) + expect(css).toMatch( + /\.n-checkbox\.control-row-checkbox\s*\{[^}]*align-items:\s*center;[^}]*min-height:\s*34px;/ + ) + expect(css).toMatch( + /\.breakdown-preview-button\.n-button\s*\{[^}]*align-self:\s*start;[^}]*margin-top:\s*var\(--breakdown-label-offset\);/ + ) + expect(css).toContain('@container breakdown-settings (max-width: 780px)') + expect(css).toContain('@container breakdown-settings (max-width: 560px)') + expect(css).toContain('repeat(auto-fit, minmax(min(100%, 160px), 1fr))') + expect(readFileSync('src/styles.css', 'utf8')).not.toContain('.breakdown-config') + }) + it.each(['人物', '场景', '道具'])('%s 使用共用固定斑马纹样式,搜索后保持列表条目结构', async label => { mountPage() await selectTab(label) diff --git a/src/features/breakdown/BreakdownPage.vue b/src/features/breakdown/BreakdownPage.vue index 70b024f..967b736 100644 --- a/src/features/breakdown/BreakdownPage.vue +++ b/src/features/breakdown/BreakdownPage.vue @@ -155,7 +155,7 @@ function exportResult() {
-
+

拆解设置

@@ -168,38 +168,46 @@ function exportResult() { 来源:正式剧集
-
+
-
+
集 / 组 + >集 / 组
-
- 抽取模块 - {{ option.label }}{{ - option.description - }} +
+ 抽取模块 +
+
+ {{ option.label }} +

+ {{ option.description }} +

+
+
预览分组
diff --git a/src/styles.css b/src/styles.css index 261b408..664edde 100644 --- a/src/styles.css +++ b/src/styles.css @@ -621,13 +621,6 @@ monospace; } - .breakdown-config { - display: grid; - grid-template-columns: 145px minmax(0, 1fr) auto; - align-items: start; - gap: 28px; - } - .group-preview { margin-top: 24px; padding-top: 20px; @@ -735,15 +728,6 @@ .history-panel { border-left: 0; } - - .breakdown-config { - grid-template-columns: 120px minmax(0, 1fr); - gap: 20px; - } - .breakdown-config > button { - grid-column: 1 / -1; - justify-self: start; - } } @media (max-width: 760px) { .storyboard-controls { @@ -771,10 +755,6 @@ font-size: 23px; } - .breakdown-config { - grid-template-columns: minmax(0, 1fr); - } - .stage-strip { gap: 9px; }